コード例 #1
0
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);
}
コード例 #2
0
ファイル: ot-builtin-checkout.c プロジェクト: 14rcole/ostree
gboolean
ostree_builtin_checkout (int argc, char **argv, GCancellable *cancellable, GError **error)
{
  GOptionContext *context;
  glnx_unref_object OstreeRepo *repo = NULL;
  gboolean ret = FALSE;
  const char *commit;
  const char *destination;
  g_autofree char *resolved_commit = NULL;

  context = g_option_context_new ("COMMIT [DESTINATION] - Check out a commit into a filesystem tree");

  if (!ostree_option_context_parse (context, options, &argc, &argv, OSTREE_BUILTIN_FLAG_NONE, &repo, cancellable, error))
    goto out;

  if (opt_disable_fsync)
    ostree_repo_set_disable_fsync (repo, TRUE);

  if (argc < 2)
    {
      gchar *help = g_option_context_get_help (context, TRUE, NULL);
      g_printerr ("%s\n", help);
      g_free (help);
      g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED,
                           "COMMIT must be specified");
      goto out;
    }

  if (opt_from_stdin || opt_from_file)
    {
      destination = argv[1];

      if (!process_many_checkouts (repo, destination, cancellable, error))
        goto out;
    }
  else
    {
      commit = argv[1];
      if (argc < 3)
        destination = commit;
      else
        destination = argv[2];

      if (!ostree_repo_resolve_rev (repo, commit, FALSE, &resolved_commit, error))
        goto out;

      if (!process_one_checkout (repo, resolved_commit, opt_subpath,
                                 destination,
                                 cancellable, error))
        goto out;
    }

  ret = TRUE;
 out:
  if (context)
    g_option_context_free (context);
  return ret;
}
コード例 #3
0
ファイル: ot-builtin-reset.c プロジェクト: endlessm/ostree
gboolean
ostree_builtin_reset (int           argc,
                      char        **argv,
                      OstreeCommandInvocation *invocation,
                      GCancellable *cancellable,
                      GError      **error)
{
  g_autoptr(GOptionContext) context = NULL;
  g_autoptr(OstreeRepo) repo = NULL;
  g_autoptr(GHashTable) known_refs = NULL;
  gboolean ret = FALSE;
  const char *ref;
  const char *target = NULL;
  g_autofree char *checksum = NULL;

  /* FIXME: Add support for collection–refs. */
  context = g_option_context_new ("REF COMMIT");

  if (!ostree_option_context_parse (context, options, &argc, &argv, invocation, &repo, cancellable, error))
    goto out;

  if (!ostree_ensure_repo_writable (repo, error))
    goto out;

  if (argc <= 2)
    {
      ot_util_usage_error (context, "A REF and COMMIT argument is required", error);
      goto out;
    }
  ref = argv[1];
  target = argv[2];

  if (!ostree_repo_list_refs (repo, NULL, &known_refs, cancellable, error))
    goto out;

  if (!g_hash_table_contains (known_refs, ref))
    {
      g_set_error (error, G_IO_ERROR, G_IO_ERROR, "Invalid ref '%s'", ref);
      goto out;
    }

  if (!ostree_repo_resolve_rev (repo, target, FALSE, &checksum, error))
    goto out;

  if (!ostree_repo_prepare_transaction (repo, NULL, cancellable, error))
    goto out;

  ostree_repo_transaction_set_ref (repo, NULL, ref, checksum);

  if (!ostree_repo_commit_transaction (repo, NULL, cancellable, error))
    goto out;

  ret = TRUE;
 out:
  if (repo)
    ostree_repo_abort_transaction (repo, cancellable, NULL);
  return ret;
}
コード例 #4
0
/**
 * 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;
}
コード例 #5
0
struct RpmRevisionData *
rpmrev_new (OstreeRepo *repo, const char *rev,
            const GPtrArray *patterns,
            GCancellable   *cancellable,
            GError        **error)
{
  struct RpmRevisionData *rpmrev = NULL;
  g_autofree char *commit = NULL;
  g_autoptr(RpmOstreeRefTs) refts = NULL;

  if (!ostree_repo_resolve_rev (repo, rev, FALSE, &commit, error))
    goto out;

  if (!rpmostree_get_refts_for_commit (repo, commit, &refts, cancellable, error))
    goto out;

  rpmrev = g_malloc0 (sizeof(struct RpmRevisionData));
  rpmrev->refts = g_steal_pointer (&refts);
  rpmrev->commit = g_steal_pointer (&commit);
  rpmrev->rpmdb = rpmhdrs_new (rpmrev->refts, patterns);

 out:
  return rpmrev;
}
コード例 #6
0
gboolean
ostree_builtin_commit (int argc, char **argv, GCancellable *cancellable, GError **error)
{
  GOptionContext *context;
  glnx_unref_object OstreeRepo *repo = NULL;
  gboolean ret = FALSE;
  gboolean skip_commit = FALSE;
  g_autoptr(GFile) object_to_commit = NULL;
  g_autofree char *parent = NULL;
  g_autofree char *commit_checksum = NULL;
  g_autoptr(GFile) root = NULL;
  g_autoptr(GVariant) metadata = NULL;
  g_autoptr(GVariant) detached_metadata = NULL;
  glnx_unref_object OstreeMutableTree *mtree = NULL;
  g_autofree char *tree_type = NULL;
  g_autoptr(GHashTable) mode_adds = NULL;
  OstreeRepoCommitModifierFlags flags = 0;
  OstreeRepoCommitModifier *modifier = NULL;
  OstreeRepoTransactionStats stats;

  context = g_option_context_new ("[PATH] - Commit a new revision");

  if (!ostree_option_context_parse (context, options, &argc, &argv, OSTREE_BUILTIN_FLAG_NONE, &repo, cancellable, error))
    goto out;

  if (!ostree_ensure_repo_writable (repo, error))
    goto out;

  if (opt_statoverride_file)
    {
      if (!parse_statoverride_file (&mode_adds, cancellable, error))
        goto out;
    }

  if (opt_metadata_strings)
    {
      if (!parse_keyvalue_strings (opt_metadata_strings,
                                   &metadata, error))
        goto out;
    }
  if (opt_detached_metadata_strings)
    {
      if (!parse_keyvalue_strings (opt_detached_metadata_strings,
                                   &detached_metadata, error))
        goto out;
    }
      
  if (!opt_branch)
    {
      g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED,
                           "A branch must be specified with --branch");
      goto out;
    }

  if (opt_no_xattrs)
    flags |= OSTREE_REPO_COMMIT_MODIFIER_FLAGS_SKIP_XATTRS;
  if (opt_generate_sizes)
    flags |= OSTREE_REPO_COMMIT_MODIFIER_FLAGS_GENERATE_SIZES;
  if (opt_disable_fsync)
    ostree_repo_set_disable_fsync (repo, TRUE);

  if (flags != 0
      || opt_owner_uid >= 0
      || opt_owner_gid >= 0
      || opt_statoverride_file != NULL
      || opt_no_xattrs)
    {
      modifier = ostree_repo_commit_modifier_new (flags, commit_filter, mode_adds, NULL);
    }

  if (!ostree_repo_resolve_rev (repo, opt_branch, TRUE, &parent, error))
    goto out;

  if (!opt_subject && !opt_body)
    {
      if (!commit_editor (repo, opt_branch, &opt_subject, &opt_body, cancellable, error))
        goto out;
    }

  if (!opt_subject)
    {
      g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED,
                           "A subject must be specified with --subject");
      goto out;
    }

  if (!ostree_repo_prepare_transaction (repo, NULL, cancellable, error))
    goto out;

  if (opt_link_checkout_speedup && !ostree_repo_scan_hardlinks (repo, cancellable, error))
    goto out;

  mtree = ostree_mutable_tree_new ();

  if (argc <= 1 && (opt_trees == NULL || opt_trees[0] == NULL))
    {
      char *current_dir = g_get_current_dir ();
      object_to_commit = g_file_new_for_path (current_dir);
      g_free (current_dir);

      if (!ostree_repo_write_directory_to_mtree (repo, object_to_commit, mtree, modifier,
                                                 cancellable, error))
        goto out;
    }
  else if (opt_trees != NULL)
    {
      const char *const*tree_iter;
      const char *tree;
      const char *eq;

      for (tree_iter = (const char *const*)opt_trees; *tree_iter; tree_iter++)
        {
          tree = *tree_iter;

          eq = strchr (tree, '=');
          if (!eq)
            {
              g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
                           "Missing type in tree specification '%s'", tree);
              goto out;
            }
          g_free (tree_type);
          tree_type = g_strndup (tree, eq - tree);
          tree = eq + 1;

          g_clear_object (&object_to_commit);
          if (strcmp (tree_type, "dir") == 0)
            {
              object_to_commit = g_file_new_for_path (tree);
              if (!ostree_repo_write_directory_to_mtree (repo, object_to_commit, mtree, modifier,
                                                         cancellable, error))
                goto out;
            }
          else if (strcmp (tree_type, "tar") == 0)
            {
              object_to_commit = g_file_new_for_path (tree);
              if (!ostree_repo_write_archive_to_mtree (repo, object_to_commit, mtree, modifier,
                                                       opt_tar_autocreate_parents,
                                                       cancellable, error))
                goto out;
            }
          else if (strcmp (tree_type, "ref") == 0)
            {
              if (!ostree_repo_read_commit (repo, tree, &object_to_commit, NULL, cancellable, error))
                goto out;

              if (!ostree_repo_write_directory_to_mtree (repo, object_to_commit, mtree, modifier,
                                                         cancellable, error))
                goto out;
            }
          else
            {
              g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
                           "Invalid tree type specification '%s'", tree_type);
              goto out;
            }
        }
    }
  else
    {
      g_assert (argc > 1);
      object_to_commit = g_file_new_for_path (argv[1]);
      if (!ostree_repo_write_directory_to_mtree (repo, object_to_commit, mtree, modifier,
                                                 cancellable, error))
        goto out;
    }

  if (mode_adds && g_hash_table_size (mode_adds) > 0)
    {
      GHashTableIter hash_iter;
      gpointer key, value;

      g_hash_table_iter_init (&hash_iter, mode_adds);

      while (g_hash_table_iter_next (&hash_iter, &key, &value))
        {
          g_printerr ("Unmatched statoverride path: %s\n", (char*)key);
        }
      g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
                   "Unmatched statoverride paths");
      goto out;
    }

  if (!ostree_repo_write_mtree (repo, mtree, &root, cancellable, error))
    goto out;

  if (opt_skip_if_unchanged && parent)
    {
      g_autoptr(GFile) parent_root;

      if (!ostree_repo_read_commit (repo, parent, &parent_root, NULL, cancellable, error))
        goto out;

      if (g_file_equal (root, parent_root))
        skip_commit = TRUE;
    }

  if (!skip_commit)
    {
      gboolean update_summary;
      if (!ostree_repo_write_commit (repo, parent, opt_subject, opt_body, metadata,
                                     OSTREE_REPO_FILE (root),
                                     &commit_checksum, cancellable, error))
        goto out;

      if (detached_metadata)
        {
          if (!ostree_repo_write_commit_detached_metadata (repo, commit_checksum,
                                                           detached_metadata,
                                                           cancellable, error))
            goto out;
        }

      if (opt_key_ids)
        {
          char **iter;

          for (iter = opt_key_ids; iter && *iter; iter++)
            {
              const char *keyid = *iter;

              if (!ostree_repo_sign_commit (repo,
                                            commit_checksum,
                                            keyid,
                                            opt_gpg_homedir,
                                            cancellable,
                                            error))
                goto out;
            }
        }

      ostree_repo_transaction_set_ref (repo, NULL, opt_branch, commit_checksum);

      if (!ostree_repo_commit_transaction (repo, &stats, cancellable, error))
        goto out;

      /* The default for this option is FALSE, even for archive-z2 repos,
       * because ostree supports multiple processes committing to the same
       * repo (but different refs) concurrently, and in fact gnome-continuous
       * actually does this.  In that context it's best to update the summary
       * explicitly instead of automatically here. */
      if (!ot_keyfile_get_boolean_with_default (ostree_repo_get_config (repo), "core",
                                                "commit-update-summary", FALSE,
                                                &update_summary, error))
        goto out;

      if (update_summary && !ostree_repo_regenerate_summary (repo,
                                                             NULL,
                                                             cancellable,
                                                             error))
        goto out;
    }
  else
    {
      commit_checksum = g_strdup (parent);
    }

  if (opt_table_output)
    {
      g_print ("Commit: %s\n", commit_checksum);
      g_print ("Metadata Total: %u\n", stats.metadata_objects_total);
      g_print ("Metadata Written: %u\n", stats.metadata_objects_written);
      g_print ("Content Total: %u\n", stats.content_objects_total);
      g_print ("Content Written: %u\n", stats.content_objects_written);
      g_print ("Content Bytes Written: %" G_GUINT64_FORMAT "\n", stats.content_bytes_written);
    }
  else
    {
      g_print ("%s\n", commit_checksum);
    }

  ret = TRUE;
 out:
  if (repo)
    ostree_repo_abort_transaction (repo, cancellable, NULL);
  if (context)
    g_option_context_free (context);
  if (modifier)
    ostree_repo_commit_modifier_unref (modifier);
  return ret;
}
コード例 #7
0
ファイル: ostree-repo-refs.c プロジェクト: jeremycline/ostree
/**
 * ostree_repo_resolve_rev:
 * @self: Repo
 * @refspec: A refspec
 * @allow_noent: Do not throw an error if refspec does not exist
 * @out_rev: (out) (transfer full): A checksum,or %NULL if @allow_noent is true and it does not exist
 * @error: Error
 *
 * Look up the given refspec, returning the checksum it references in
 * the parameter @out_rev.
 */
gboolean
ostree_repo_resolve_rev (OstreeRepo     *self,
                         const char     *refspec,
                         gboolean        allow_noent,
                         char          **out_rev,
                         GError        **error)
{
    gboolean ret = FALSE;
    g_autofree char *ret_rev = NULL;

    g_return_val_if_fail (refspec != NULL, FALSE);

    if (ostree_validate_checksum_string (refspec, NULL))
    {
        ret_rev = g_strdup (refspec);
    }

    else if (!ostree_repo_resolve_partial_checksum (self, refspec, &ret_rev, error))
        goto out;

    if (!ret_rev)
    {
        if (error != NULL && *error != NULL)
            goto out;

        if (g_str_has_suffix (refspec, "^"))
        {
            g_autofree char *parent_refspec = NULL;
            g_autofree char *parent_rev = NULL;
            g_autoptr(GVariant) commit = NULL;

            parent_refspec = g_strdup (refspec);
            parent_refspec[strlen(parent_refspec) - 1] = '\0';

            if (!ostree_repo_resolve_rev (self, parent_refspec, allow_noent, &parent_rev, error))
                goto out;

            if (!ostree_repo_load_variant (self, OSTREE_OBJECT_TYPE_COMMIT, parent_rev,
                                           &commit, error))
                goto out;

            if (!(ret_rev = ostree_commit_get_parent (commit)))
            {
                g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
                             "Commit %s has no parent", parent_rev);
                goto out;
            }
        }
        else
        {
            g_autofree char *remote = NULL;
            g_autofree char *ref = NULL;

            if (!ostree_parse_refspec (refspec, &remote, &ref, error))
                goto out;

            if (!resolve_refspec (self, remote, ref, allow_noent,
                                  &ret_rev, error))
                goto out;
        }
    }

    ret = TRUE;
    ot_transfer_out_value (out_rev, &ret_rev);
out:
    return ret;
}
コード例 #8
0
gboolean
rpmostree_container_builtin_upgrade (int argc, char **argv, GCancellable *cancellable, GError **error)
{
  int exit_status = EXIT_FAILURE;
  GOptionContext *context = g_option_context_new ("NAME");
  g_auto(ROContainerContext) rocctx_data = RO_CONTAINER_CONTEXT_INIT;
  ROContainerContext *rocctx = &rocctx_data;
  g_autoptr(RpmOstreeInstall) install = NULL;
  const char *name;
  g_autofree char *commit_checksum = NULL;
  g_autofree char *new_commit_checksum = NULL;
  g_autoptr(GVariant) commit = NULL;
  g_autoptr(GVariant) metadata = NULL;
  g_autoptr(GVariant) input_packages_v = NULL;
  g_autoptr(RpmOstreeTreespec) treespec = NULL;
  guint current_version;
  guint new_version;
  g_autofree char *previous_state_sha512 = NULL;
  const char *target_current_root;
  const char *target_new_root;
  
  if (!rpmostree_option_context_parse (context,
                                       assemble_option_entries,
                                       &argc, &argv,
                                       RPM_OSTREE_BUILTIN_FLAG_LOCAL_CMD,
                                       cancellable,
                                       NULL,
                                       error))
    goto out;

  if (argc < 1)
    {
      rpmostree_usage_error (context, "NAME must be specified", error);
      goto out;
    }

  name = argv[1];

  if (!roc_context_init (rocctx, error))
    goto out;

  target_current_root = glnx_readlinkat_malloc (rocctx->roots_dfd, name, cancellable, error);
  if (!target_current_root)
    {
      g_prefix_error (error, "Reading app link %s: ", name);
      goto out;
    }

  if (!parse_app_version (target_current_root, &current_version, error))
    goto out;

  { g_autoptr(GVariantDict) metadata_dict = NULL;
    g_autoptr(GVariant) spec_v = NULL;
    g_autoptr(GVariant) previous_sha512_v = NULL;

    if (!ostree_repo_resolve_rev (rocctx->repo, name, FALSE, &commit_checksum, error))
      goto out;

    if (!ostree_repo_load_variant (rocctx->repo, OSTREE_OBJECT_TYPE_COMMIT, commit_checksum,
                                   &commit, error))
      goto out;

    metadata = g_variant_get_child_value (commit, 0);
    metadata_dict = g_variant_dict_new (metadata);

    spec_v = _rpmostree_vardict_lookup_value_required (metadata_dict, "rpmostree.spec",
                                                                 (GVariantType*)"a{sv}", error);
    if (!spec_v)
      goto out;

    treespec = rpmostree_treespec_new (spec_v);

    previous_sha512_v = _rpmostree_vardict_lookup_value_required (metadata_dict,
                                                                  "rpmostree.state-sha512",
                                                                  (GVariantType*)"s", error);
    if (!previous_sha512_v)
      goto out;

    previous_state_sha512 = g_variant_dup_string (previous_sha512_v, NULL);
  }

  new_version = current_version == 0 ? 1 : 0;
  if (new_version == 0)
    target_new_root = glnx_strjoina (name, ".0");
  else
    target_new_root = glnx_strjoina (name, ".1");

  if (!roc_context_prepare_for_root (rocctx, name, treespec, cancellable, error))
    goto out;

  /* --- Downloading metadata --- */
  if (!rpmostree_context_download_metadata (rocctx->ctx, cancellable, error))
    goto out;

  /* --- Resolving dependencies --- */
  if (!rpmostree_context_prepare_install (rocctx->ctx, &install,
                                          cancellable, error))
    goto out;

  { g_autofree char *new_state_sha512 = rpmostree_context_get_state_sha512 (rocctx->ctx);

    if (strcmp (new_state_sha512, previous_state_sha512) == 0)
      {
        g_print ("No changes in inputs to %s (%s)\n", name, commit_checksum);
        exit_status = EXIT_SUCCESS;
        goto out;
      }
  }

  /* --- Download and import as necessary --- */
  if (!rpmostree_context_download_import (rocctx->ctx, install,
                                          cancellable, error))
    goto out;

  { glnx_fd_close int tmpdir_dfd = -1;

    if (!glnx_opendirat (rocctx->userroot_dfd, "tmp", TRUE, &tmpdir_dfd, error))
      goto out;
    
    if (!rpmostree_context_assemble_commit (rocctx->ctx, tmpdir_dfd,
                                            name,
                                            install,
                                            &new_commit_checksum,
                                            cancellable, error))
      goto out;
  }

  g_print ("Checking out %s @ %s...\n", name, new_commit_checksum);

  { OstreeRepoCheckoutOptions opts = { OSTREE_REPO_CHECKOUT_MODE_USER,
                                       OSTREE_REPO_CHECKOUT_OVERWRITE_UNION_FILES, };

    /* For now... to be crash safe we'd need to duplicate some of the
     * boot-uuid/fsync gating at a higher level.
     */
    opts.disable_fsync = TRUE;

    if (!ostree_repo_checkout_tree_at (rocctx->repo, &opts, rocctx->roots_dfd, target_new_root,
                                       new_commit_checksum, cancellable, error))
      goto out;
  }

  g_print ("Checking out %s @ %s...done\n", name, new_commit_checksum);

  if (!symlink_at_replace (target_new_root, rocctx->roots_dfd, name,
                           cancellable, error))
    goto out;

  g_print ("Creating current symlink...done\n");

  exit_status = EXIT_SUCCESS;
 out:
  return exit_status;
}
コード例 #9
0
gboolean
rpmostree_compose_builtin_sign (int            argc,
                                char         **argv,
                                GCancellable  *cancellable,
                                GError       **error)
{
  gboolean ret = FALSE;
  GOptionContext *context = g_option_context_new ("- Use rpm-sign to sign an OSTree commit");
  gs_unref_object GFile *repopath = NULL;
  gs_unref_object OstreeRepo *repo = NULL;
  gs_unref_object GFile *tmp_commitdata_file = NULL;
  gs_unref_object GFileIOStream *tmp_sig_stream = NULL;
  gs_unref_object GFile *tmp_sig_file = NULL;
  gs_unref_object GFileIOStream *tmp_commitdata_stream = NULL;
  GOutputStream *tmp_commitdata_output = NULL;
  gs_unref_object GInputStream *commit_data = NULL;
  gs_free char *checksum = NULL;
  gs_unref_variant GVariant *commit_variant = NULL;
  gs_unref_bytes GBytes *commit_bytes = NULL;
  
  if (!rpmostree_option_context_parse (context, option_entries, &argc, &argv, error))
    goto out;

  if (!(opt_repo_path && opt_key_id && opt_rev))
    {
      g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
                   "Missing required argument");
      goto out;
    }

  repopath = g_file_new_for_path (opt_repo_path);
  repo = ostree_repo_new (repopath);
  if (!ostree_repo_open (repo, cancellable, error))
    goto out;

  if (!ostree_repo_resolve_rev (repo, opt_rev, FALSE, &checksum, error))
    goto out;

  if (!ostree_repo_load_variant (repo, OSTREE_OBJECT_TYPE_COMMIT,
                                 checksum, &commit_variant, error))
    goto out;

  commit_bytes = g_variant_get_data_as_bytes (commit_variant);
  commit_data = (GInputStream*)g_memory_input_stream_new_from_bytes (commit_bytes);
  
  tmp_commitdata_file = g_file_new_tmp ("tmpsigXXXXXX", &tmp_commitdata_stream,
                                       error);
  if (!tmp_commitdata_file)
    goto out;

  tmp_commitdata_output = (GOutputStream*)g_io_stream_get_output_stream ((GIOStream*)tmp_commitdata_stream);
  if (g_output_stream_splice ((GOutputStream*)tmp_commitdata_output,
                              commit_data,
                              G_OUTPUT_STREAM_SPLICE_CLOSE_SOURCE |
                              G_OUTPUT_STREAM_SPLICE_CLOSE_TARGET,
                              cancellable, error) < 0)
    goto out;

  tmp_sig_file = g_file_new_tmp ("tmpsigoutXXXXXX", &tmp_sig_stream, error);
  if (!tmp_sig_file)
    goto out;

  (void) g_io_stream_close ((GIOStream*)tmp_sig_stream, NULL, NULL);
                                  
  if (!gs_subprocess_simple_run_sync (NULL, GS_SUBPROCESS_STREAM_DISPOSITION_NULL,
                                      cancellable, error,
                                      "rpm-sign",
                                      "--key", opt_key_id,
                                      "--detachsign", gs_file_get_path_cached (tmp_commitdata_file),
                                      "--output", gs_file_get_path_cached (tmp_sig_file),
                                      NULL))
    goto out;

  {
    char *sigcontent = NULL;
    gsize len;
    gs_unref_bytes GBytes *sigbytes = NULL;

    if (!g_file_load_contents (tmp_sig_file, cancellable, &sigcontent, &len, NULL,
                               error))
      goto out;

    sigbytes = g_bytes_new_take (sigcontent, len);

    if (!ostree_repo_append_gpg_signature (repo, checksum, sigbytes,
                                           cancellable, error))
      goto out;
  }

  g_print ("Successfully signed OSTree commit=%s with key=%s\n",
           checksum, opt_key_id);
  
  ret = TRUE;
 out:
  if (tmp_commitdata_file)
    (void) gs_file_unlink (tmp_commitdata_file, NULL, NULL);
  if (tmp_sig_file)
    (void) gs_file_unlink (tmp_sig_file, NULL, NULL);
  return ret;
}
コード例 #10
0
ファイル: ot-builtin-show.c プロジェクト: UIKit0/ostree
gboolean
ostree_builtin_show (int argc, char **argv, OstreeRepo *repo, GCancellable *cancellable, GError **error)
{
  GOptionContext *context;
  gboolean ret = FALSE;
  const char *rev;
  gs_free char *resolved_rev = NULL;

  context = g_option_context_new ("OBJECT - Output a metadata object");
  g_option_context_add_main_entries (context, options, NULL);

  if (!g_option_context_parse (context, &argc, &argv, error))
    goto out;

  if (argc <= 1)
    {
      ot_util_usage_error (context, "An object argument is required", error);
      goto out;
    }
  rev = argv[1];

  if (opt_print_metadata_key || opt_print_detached_metadata_key)
    {
      gboolean detached = opt_print_detached_metadata_key != NULL;
      const char *key = detached ? opt_print_detached_metadata_key : opt_print_metadata_key;
      if (!ostree_repo_resolve_rev (repo, rev, FALSE, &resolved_rev, error))
        goto out;

      if (!do_print_metadata_key (repo, resolved_rev, detached, key, error))
        goto out;
    }
  else if (opt_print_related)
    {
      if (!ostree_repo_resolve_rev (repo, rev, FALSE, &resolved_rev, error))
        goto out;

      if (!do_print_related (repo, rev, resolved_rev, error))
        goto out;
    }
  else if (opt_print_variant_type)
    {
      if (!do_print_variant_generic (G_VARIANT_TYPE (opt_print_variant_type), rev, error))
        goto out;
    }
  else
    {
      gboolean found = FALSE;
      if (!ostree_validate_checksum_string (rev, NULL))
        {
          if (!ostree_repo_resolve_rev (repo, rev, FALSE, &resolved_rev, error))
            goto out;
          if (!print_object (repo, OSTREE_OBJECT_TYPE_COMMIT, resolved_rev, error))
            goto out;
        }
      else
        {
          if (!print_if_found (repo, OSTREE_OBJECT_TYPE_COMMIT, rev,
                               &found, cancellable, error))
            goto out;
          if (!print_if_found (repo, OSTREE_OBJECT_TYPE_DIR_META, rev,
                               &found, cancellable, error))
            goto out;
          if (!print_if_found (repo, OSTREE_OBJECT_TYPE_DIR_TREE, rev,
                               &found, cancellable, error))
            goto out;
          if (!found)
            {
              gs_unref_object GFileInfo *finfo = NULL;
              gs_unref_variant GVariant *xattrs = NULL;
              GFileType filetype;
              
              if (!ostree_repo_load_file (repo, resolved_rev, NULL, &finfo, &xattrs,
                                          cancellable, error))
                goto out;

              g_print ("Object: %s\nType: %s\n", rev, ostree_object_type_to_string (OSTREE_OBJECT_TYPE_FILE));
              filetype = g_file_info_get_file_type (finfo);
              g_print ("File Type: ");
              switch (filetype)
                {
                case G_FILE_TYPE_REGULAR:
                  g_print ("regular\n");
                  g_print ("Size: %" G_GUINT64_FORMAT "\n", g_file_info_get_size (finfo));
                  break;
                case G_FILE_TYPE_SYMBOLIC_LINK:
                  g_print ("symlink\n");
                  g_print ("Target: %s\n", g_file_info_get_symlink_target (finfo));
                  break;
                default:
                  g_printerr ("(unknown type %u)\n", (guint)filetype);
                }

              g_print ("Mode: 0%04o\n", g_file_info_get_attribute_uint32 (finfo, "unix::mode"));
              g_print ("Uid: %u\n", g_file_info_get_attribute_uint32 (finfo, "unix::uid"));
              g_print ("Gid: %u\n", g_file_info_get_attribute_uint32 (finfo, "unix::gid"));

              g_print ("Extended Attributes: ");
              if (xattrs)
                {
                  gs_free char *xattr_string = g_variant_print (xattrs, TRUE);
                  g_print ("{ %s }\n", xattr_string);
                }
              else
                {
                  g_print ("(none)\n");
                }
            }
        }
    }
 
  ret = TRUE;
 out:
  if (context)
    g_option_context_free (context);
  return ret;
}
コード例 #11
0
gboolean
flatpak_builtin_build_sign (int argc, char **argv, GCancellable *cancellable, GError **error)
{
  g_autoptr(GOptionContext) context = NULL;
  g_autoptr(GFile) repofile = NULL;
  g_autoptr(OstreeRepo) repo = NULL;
  const char *location;
  const char *branch;
  const char *id;
  g_autofree char *commit_checksum = NULL;
  g_autofree char *ref = NULL;
  char **iter;

  context = g_option_context_new ("LOCATION ID [BRANCH] - Create a repository from a build directory");

  if (!flatpak_option_context_parse (context, options, &argc, &argv, FLATPAK_BUILTIN_FLAG_NO_DIR, NULL, cancellable, error))
    return FALSE;

  if (argc < 3)
    {
      usage_error (context, "LOCATION and DIRECTORY must be specified", error);
      return FALSE;
    }

  location = argv[1];
  id = argv[2];

  if (argc >= 4)
    branch = argv[3];
  else
    branch = "master";

  if (!flatpak_is_valid_name (id))
    return flatpak_fail (error, "'%s' is not a valid name", id);

  if (!flatpak_is_valid_branch (branch))
    return flatpak_fail (error, "'%s' is not a valid branch name", branch);

  if (opt_gpg_key_ids == NULL)
    return flatpak_fail (error, "No gpg key ids specified");

  if (opt_runtime)
    ref = flatpak_build_runtime_ref (id, branch, opt_arch);
  else
    ref = flatpak_build_app_ref (id, branch, opt_arch);

  repofile = g_file_new_for_commandline_arg (location);
  repo = ostree_repo_new (repofile);

  if (!ostree_repo_open (repo, cancellable, error))
    return FALSE;

  if (!ostree_repo_resolve_rev (repo, ref, TRUE, &commit_checksum, error))
    return FALSE;

  for (iter = opt_gpg_key_ids; iter && *iter; iter++)
    {
      const char *keyid = *iter;

      if (!ostree_repo_sign_commit (repo,
                                    commit_checksum,
                                    keyid,
                                    opt_gpg_homedir,
                                    cancellable,
                                    error))
        return FALSE;
    }

  return TRUE;
}
コード例 #12
0
ファイル: ostree-repo-refs.c プロジェクト: alexlarsson/ostree
static gboolean
_ostree_repo_resolve_rev_internal (OstreeRepo     *self,
                                   const char     *refspec,
                                   gboolean        allow_noent,
                                   gboolean        fallback_remote,
                                   char          **out_rev,
                                   GError        **error)
{
  g_autofree char *ret_rev = NULL;

  g_return_val_if_fail (refspec != NULL, FALSE);

  if (ostree_validate_checksum_string (refspec, NULL))
    {
      ret_rev = g_strdup (refspec);
    }

  else if (!ostree_repo_resolve_partial_checksum (self, refspec, &ret_rev, error))
    return FALSE;

  if (!ret_rev)
    {
      if (error != NULL && *error != NULL)
        return FALSE;

      if (g_str_has_suffix (refspec, "^"))
        {
          g_autofree char *parent_refspec = NULL;
          g_autofree char *parent_rev = NULL;
          g_autoptr(GVariant) commit = NULL;

          parent_refspec = g_strdup (refspec);
          parent_refspec[strlen(parent_refspec) - 1] = '\0';

          if (!ostree_repo_resolve_rev (self, parent_refspec, allow_noent, &parent_rev, error))
            return FALSE;

          if (!ostree_repo_load_variant (self, OSTREE_OBJECT_TYPE_COMMIT, parent_rev,
                                         &commit, error))
            return FALSE;

          if (!(ret_rev = ostree_commit_get_parent (commit)))
            return glnx_throw (error, "Commit %s has no parent", parent_rev);
        }
      else
        {
          g_autofree char *remote = NULL;
          g_autofree char *ref = NULL;

          if (!ostree_parse_refspec (refspec, &remote, &ref, error))
            return FALSE;

          if (!resolve_refspec (self, remote, ref, allow_noent,
                                fallback_remote, &ret_rev, error))
            return FALSE;
        }
    }

  ot_transfer_out_value (out_rev, &ret_rev);
  return TRUE;
}
コード例 #13
0
int
rpmostree_compose_builtin_sign (int            argc,
                                char         **argv,
                                GCancellable  *cancellable,
                                GError       **error)
{
  int exit_status = EXIT_FAILURE;
  GOptionContext *context = g_option_context_new ("- Use rpm-sign to sign an OSTree commit");
  g_autoptr(GFile) repopath = NULL;
  glnx_unref_object OstreeRepo *repo = NULL;
  g_autoptr(GFile) tmp_commitdata_file = NULL;
  g_autoptr(GFileIOStream) tmp_sig_stream = NULL;
  g_autoptr(GFile) tmp_sig_file = NULL;
  g_autoptr(GFileIOStream) tmp_commitdata_stream = NULL;
  GOutputStream *tmp_commitdata_output = NULL;
  g_autoptr(GInputStream) commit_data = NULL;
  g_autofree char *checksum = NULL;
  g_autoptr(GVariant) commit_variant = NULL;
  g_autoptr(GBytes) commit_bytes = NULL;
  
  if (!rpmostree_option_context_parse (context,
                                       option_entries,
                                       &argc, &argv,
                                       RPM_OSTREE_BUILTIN_FLAG_LOCAL_CMD,
                                       cancellable,
                                       NULL,
                                       error))
    goto out;

  if (!(opt_repo_path && opt_key_id && opt_rev))
    {
      rpmostree_usage_error (context, "Missing required argument", error);
      goto out;
    }

  repopath = g_file_new_for_path (opt_repo_path);
  repo = ostree_repo_new (repopath);
  if (!ostree_repo_open (repo, cancellable, error))
    goto out;

  if (!ostree_repo_resolve_rev (repo, opt_rev, FALSE, &checksum, error))
    goto out;

  if (!ostree_repo_load_variant (repo, OSTREE_OBJECT_TYPE_COMMIT,
                                 checksum, &commit_variant, error))
    goto out;

  commit_bytes = g_variant_get_data_as_bytes (commit_variant);
  commit_data = (GInputStream*)g_memory_input_stream_new_from_bytes (commit_bytes);
  
  tmp_commitdata_file = g_file_new_tmp ("tmpsigXXXXXX", &tmp_commitdata_stream,
                                       error);
  if (!tmp_commitdata_file)
    goto out;

  tmp_commitdata_output = (GOutputStream*)g_io_stream_get_output_stream ((GIOStream*)tmp_commitdata_stream);
  if (g_output_stream_splice ((GOutputStream*)tmp_commitdata_output,
                              commit_data,
                              G_OUTPUT_STREAM_SPLICE_CLOSE_SOURCE |
                              G_OUTPUT_STREAM_SPLICE_CLOSE_TARGET,
                              cancellable, error) < 0)
    goto out;

  tmp_sig_file = g_file_new_tmp ("tmpsigoutXXXXXX", &tmp_sig_stream, error);
  if (!tmp_sig_file)
    goto out;

  (void) g_io_stream_close ((GIOStream*)tmp_sig_stream, NULL, NULL);
                                  

  { const char *child_argv[] = { "rpm-sign",
                                 "--key", opt_key_id,
                                 "--detachsign", gs_file_get_path_cached (tmp_commitdata_file),
                                 "--output", gs_file_get_path_cached (tmp_sig_file),
                                 NULL };
    int estatus;
    
    if (!g_spawn_sync (NULL, (char**)child_argv, NULL, G_SPAWN_SEARCH_PATH, NULL, NULL,
                       NULL, NULL, &estatus, error))
      goto out;
    if (!g_spawn_check_exit_status (estatus, error))
      goto out;
  }

  {
    char *sigcontent = NULL;
    gsize len;
    g_autoptr(GBytes) sigbytes = NULL;

    if (!g_file_load_contents (tmp_sig_file, cancellable, &sigcontent, &len, NULL,
                               error))
      goto out;

    sigbytes = g_bytes_new_take (sigcontent, len);

    if (!ostree_repo_append_gpg_signature (repo, checksum, sigbytes,
                                           cancellable, error))
      goto out;
  }

  g_print ("Successfully signed OSTree commit=%s with key=%s\n",
           checksum, opt_key_id);
  
  exit_status = EXIT_SUCCESS;

 out:
  if (tmp_commitdata_file)
    (void) unlink (gs_file_get_path_cached (tmp_commitdata_file));
  if (tmp_sig_file)
    (void) unlink (gs_file_get_path_cached (tmp_sig_file));

  return exit_status;
}
コード例 #14
0
ファイル: ot-builtin-diff.c プロジェクト: endlessm/ostree
gboolean
ostree_builtin_diff (int argc, char **argv, OstreeCommandInvocation *invocation, GCancellable *cancellable, GError **error)
{
  gboolean ret = FALSE;
  g_autoptr(GOptionContext) context = NULL;
  g_autoptr(OstreeRepo) repo = NULL;
  const char *src;
  const char *target;
  g_autofree char *src_prev = NULL;
  g_autoptr(GFile) srcf = NULL;
  g_autoptr(GFile) targetf = NULL;
  g_autoptr(GPtrArray) modified = NULL;
  g_autoptr(GPtrArray) removed = NULL;
  g_autoptr(GPtrArray) added = NULL;

  context = g_option_context_new ("REV TARGETDIR");

  if (!ostree_option_context_parse (context, options, &argc, &argv, invocation, &repo, cancellable, error))
    goto out;

  if (argc < 2)
    {
      gchar *help = g_option_context_get_help (context, TRUE, NULL);
      g_printerr ("%s\n", help);
      g_free (help);
      g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED,
                               "REV must be specified");
      goto out;
    }

  if (argc == 2)
    {
      src_prev = g_strconcat (argv[1], "^", NULL);
      src = src_prev;
      target = argv[1];
    }
  else
    {
      src = argv[1];
      target = argv[2];
    }

  if (!opt_stats && !opt_fs_diff)
    opt_fs_diff = TRUE;

  if (opt_fs_diff)
    {
      OstreeDiffFlags diff_flags = OSTREE_DIFF_FLAGS_NONE; 

      if (opt_no_xattrs)
        diff_flags |= OSTREE_DIFF_FLAGS_IGNORE_XATTRS;
      
      if (!parse_file_or_commit (repo, src, &srcf, cancellable, error))
        goto out;
      if (!parse_file_or_commit (repo, target, &targetf, cancellable, error))
        goto out;

      modified = g_ptr_array_new_with_free_func ((GDestroyNotify)ostree_diff_item_unref);
      removed = g_ptr_array_new_with_free_func ((GDestroyNotify)g_object_unref);
      added = g_ptr_array_new_with_free_func ((GDestroyNotify)g_object_unref);

      OstreeDiffDirsOptions diff_opts = { opt_owner_uid, opt_owner_gid };
      if (!ostree_diff_dirs_with_options (diff_flags, srcf, targetf, modified, removed,
                                          added, &diff_opts, cancellable, error))
        goto out;

      ostree_diff_print (srcf, targetf, modified, removed, added);
    }

  if (opt_stats)
    {
      g_autoptr(GHashTable) reachable_a = NULL;
      g_autoptr(GHashTable) reachable_b = NULL;
      g_autoptr(GHashTable) reachable_intersection = NULL;
      g_autofree char *rev_a = NULL;
      g_autofree char *rev_b = NULL;
      g_autofree char *size = NULL;
      guint a_size;
      guint b_size;
      guint64 total_common;

      if (!ostree_repo_resolve_rev (repo, src, FALSE, &rev_a, error))
        goto out;
      if (!ostree_repo_resolve_rev (repo, target, FALSE, &rev_b, error))
        goto out;

      if (!ostree_repo_traverse_commit (repo, rev_a, 0, &reachable_a, cancellable, error))
        goto out;
      if (!ostree_repo_traverse_commit (repo, rev_b, 0, &reachable_b, cancellable, error))
        goto out;

      a_size = g_hash_table_size (reachable_a);
      b_size = g_hash_table_size (reachable_b);
      g_print ("[A] Object Count: %u\n", a_size);
      g_print ("[B] Object Count: %u\n", b_size);

      reachable_intersection = reachable_set_intersect (reachable_a, reachable_b);

      g_print ("Common Object Count: %u\n", g_hash_table_size (reachable_intersection));

      if (!object_set_total_size (repo, reachable_intersection, &total_common,
                                  cancellable, error))
        goto out;
      size = g_format_size_full (total_common, 0);
      g_print ("Common Object Size: %s\n", size);
    }

  ret = TRUE;
 out:
  return ret;
}
コード例 #15
0
/**
 * 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;
}
コード例 #16
0
gboolean
ot_admin_builtin_deploy (int argc, char **argv, OstreeSysroot *sysroot, GCancellable *cancellable, GError **error)
{
  gboolean ret = FALSE;
  const char *refspec;
  GOptionContext *context;
  GKeyFile *origin = NULL;
  gs_unref_object OstreeRepo *repo = NULL;
  gs_unref_ptrarray GPtrArray *new_deployments = NULL;
  gs_unref_object OstreeDeployment *new_deployment = NULL;
  gs_unref_object OstreeDeployment *merge_deployment = NULL;
  gs_free char *revision = NULL;
  __attribute__((cleanup(_ostree_kernel_args_cleanup))) OstreeKernelArgs *kargs = NULL;

  context = g_option_context_new ("REFSPEC - Checkout revision REFSPEC as the new default deployment");

  g_option_context_add_main_entries (context, options, NULL);

  if (!g_option_context_parse (context, &argc, &argv, error))
    goto out;

  if (argc < 2)
    {
      ot_util_usage_error (context, "REF/REV must be specified", error);
      goto out;
    }

  refspec = argv[1];

  if (!ostree_sysroot_load (sysroot, cancellable, error))
    goto out;

  if (!ostree_sysroot_get_repo (sysroot, &repo, cancellable, error))
    goto out;

  /* Find the currently booted deployment, if any; we will ensure it
   * is present in the new deployment list.
   */
  if (!ot_admin_require_booted_deployment_or_osname (sysroot, opt_osname,
                                                     cancellable, error))
    {
      g_prefix_error (error, "Looking for booted deployment: ");
      goto out;
    }

  if (opt_origin_path)
    {
      origin = g_key_file_new ();
      
      if (!g_key_file_load_from_file (origin, opt_origin_path, 0, error))
        goto out;
    }
  else
    {
      origin = ostree_sysroot_origin_new_from_refspec (sysroot, refspec);
    }

  if (!ostree_repo_resolve_rev (repo, refspec, FALSE, &revision, error))
    goto out;

  merge_deployment = ostree_sysroot_get_merge_deployment (sysroot, opt_osname);

  /* Here we perform cleanup of any leftover data from previous
   * partial failures.  This avoids having to call gs_shutil_rm_rf()
   * at random points throughout the process.
   *
   * TODO: Add /ostree/transaction file, and only do this cleanup if
   * we find it.
   */
  if (!ostree_sysroot_cleanup (sysroot, cancellable, error))
    {
      g_prefix_error (error, "Performing initial cleanup: ");
      goto out;
    }

  kargs = _ostree_kernel_args_new ();

  /* If they want the current kernel's args, they very likely don't
   * want the ones from the merge.
   */
  if (opt_kernel_proc_cmdline)
    {
      gs_unref_object GFile *proc_cmdline_path = g_file_new_for_path ("/proc/cmdline");
      gs_free char *proc_cmdline = NULL;
      gsize proc_cmdline_len = 0;
      gs_strfreev char **proc_cmdline_args = NULL;

      if (!g_file_load_contents (proc_cmdline_path, cancellable,
                                 &proc_cmdline, &proc_cmdline_len,
                                 NULL, error))
        goto out;

      g_strchomp (proc_cmdline);

      proc_cmdline_args = g_strsplit (proc_cmdline, " ", -1);
      _ostree_kernel_args_replace_argv (kargs, proc_cmdline_args);
    }
  else if (merge_deployment)
    {
      OstreeBootconfigParser *bootconfig = ostree_deployment_get_bootconfig (merge_deployment);
      gs_strfreev char **previous_args = g_strsplit (ostree_bootconfig_parser_get (bootconfig, "options"), " ", -1);

      _ostree_kernel_args_replace_argv (kargs, previous_args);
    }

  if (opt_kernel_argv)
    {
      _ostree_kernel_args_replace_argv (kargs, opt_kernel_argv);
    }

  if (opt_kernel_argv_append)
    {
      _ostree_kernel_args_append_argv (kargs, opt_kernel_argv_append);
    }

  {
    gs_strfreev char **kargs_strv = _ostree_kernel_args_to_strv (kargs);

    if (!ostree_sysroot_deploy_tree (sysroot,
                                     opt_osname, revision, origin,
                                     merge_deployment, kargs_strv,
                                     &new_deployment,
                                     cancellable, error))
      goto out;
  }

  if (!ostree_sysroot_simple_write_deployment (sysroot, opt_osname,
                                               new_deployment, merge_deployment,
                                               opt_retain ? OSTREE_SYSROOT_SIMPLE_WRITE_DEPLOYMENT_FLAGS_RETAIN : 0,
                                               cancellable, error))
    goto out;

  ret = TRUE;
 out:
  if (origin)
    g_key_file_unref (origin);
  if (context)
    g_option_context_free (context);
  return ret;
}
コード例 #17
0
ファイル: ot-builtin-diff.c プロジェクト: UIKit0/ostree
gboolean
ostree_builtin_diff (int argc, char **argv, OstreeRepo *repo, GCancellable *cancellable, GError **error)
{
  gboolean ret = FALSE;
  GOptionContext *context;
  const char *src;
  const char *target;
  gs_free char *src_prev = NULL;
  gs_unref_object GFile *srcf = NULL;
  gs_unref_object GFile *targetf = NULL;
  gs_unref_ptrarray GPtrArray *modified = NULL;
  gs_unref_ptrarray GPtrArray *removed = NULL;
  gs_unref_ptrarray GPtrArray *added = NULL;

  context = g_option_context_new ("REV TARGETDIR - Compare directory TARGETDIR against revision REV");
  g_option_context_add_main_entries (context, options, NULL);

  if (!g_option_context_parse (context, &argc, &argv, error))
    goto out;

  if (argc < 2)
    {
      gchar *help = g_option_context_get_help (context, TRUE, NULL);
      g_printerr ("%s\n", help);
      g_free (help);
      g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED,
                               "REV must be specified");
      goto out;
    }

  if (argc == 2)
    {
      src_prev = g_strconcat (argv[1], "^", NULL);
      src = src_prev;
      target = argv[1];
    }
  else
    {
      src = argv[1];
      target = argv[2];
    }

  if (!opt_stats && !opt_fs_diff)
    opt_fs_diff = TRUE;

  if (opt_fs_diff)
    {
      if (!parse_file_or_commit (repo, src, &srcf, cancellable, error))
        goto out;
      if (!parse_file_or_commit (repo, target, &targetf, cancellable, error))
        goto out;

      modified = g_ptr_array_new_with_free_func ((GDestroyNotify)ostree_diff_item_unref);
      removed = g_ptr_array_new_with_free_func ((GDestroyNotify)g_object_unref);
      added = g_ptr_array_new_with_free_func ((GDestroyNotify)g_object_unref);
      
      if (!ostree_diff_dirs (OSTREE_DIFF_FLAGS_NONE, srcf, targetf, modified, removed, added, cancellable, error))
        goto out;

      ostree_diff_print (srcf, targetf, modified, removed, added);
    }

  if (opt_stats)
    {
      gs_unref_hashtable GHashTable *reachable_a = NULL;
      gs_unref_hashtable GHashTable *reachable_b = NULL;
      gs_unref_hashtable GHashTable *reachable_intersection = NULL;
      gs_free char *rev_a = NULL;
      gs_free char *rev_b = NULL;
      gs_free char *size = NULL;
      guint a_size;
      guint b_size;
      guint64 total_common;

      if (!ostree_repo_resolve_rev (repo, src, FALSE, &rev_a, error))
        goto out;
      if (!ostree_repo_resolve_rev (repo, target, FALSE, &rev_b, error))
        goto out;

      if (!ostree_repo_traverse_commit (repo, rev_a, -1, &reachable_a, cancellable, error))
        goto out;
      if (!ostree_repo_traverse_commit (repo, rev_b, -1, &reachable_b, cancellable, error))
        goto out;

      a_size = g_hash_table_size (reachable_a);
      b_size = g_hash_table_size (reachable_b);
      g_print ("[A] Object Count: %u\n", a_size);
      g_print ("[B] Object Count: %u\n", b_size);

      reachable_intersection = reachable_set_intersect (reachable_a, reachable_b);

      g_print ("Common Object Count: %u\n", g_hash_table_size (reachable_intersection));

      if (!object_set_total_size (repo, reachable_intersection, &total_common,
                                  cancellable, error))
        goto out;
      size = g_format_size_full (total_common, 0);
      g_print ("Common Object Size: %s\n", size);
    }

  ret = TRUE;
 out:
  if (context)
    g_option_context_free (context);
  return ret;
}
コード例 #18
0
static gboolean
ot_static_delta_builtin_generate (int argc, char **argv, GCancellable *cancellable, GError **error)
{
  gboolean ret = FALSE;
  GOptionContext *context;
  glnx_unref_object OstreeRepo *repo = NULL;

  context = g_option_context_new ("Generate static delta files");
  if (!ostree_option_context_parse (context, generate_options, &argc, &argv, OSTREE_BUILTIN_FLAG_NONE, &repo, cancellable, error))
    goto out;

  if (!ostree_ensure_repo_writable (repo, error))
    goto out;

  if (argc >= 3 && opt_to_rev == NULL)
    opt_to_rev = argv[2];

  if (argc < 3 && opt_to_rev == NULL)
    {
      g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED,
                           "TO revision must be specified");
      goto out;
    }
  else
    {
      const char *from_source;
      g_autofree char *from_resolved = NULL;
      g_autofree char *to_resolved = NULL;
      g_autofree char *from_parent_str = NULL;
      g_autoptr(GVariantBuilder) parambuilder = NULL;

      g_assert (opt_to_rev);

      if (opt_empty)
        {
          if (opt_from_rev)
            {
              g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED,
                                   "Cannot specify both --empty and --from=REV");
              goto out;
            }
          from_source = NULL;
        }
      else if (opt_from_rev == NULL)
        {
          from_parent_str = g_strconcat (opt_to_rev, "^", NULL);
          from_source = from_parent_str;
        }
      else
        {
          from_source = opt_from_rev;
        }

      if (from_source)
        {
          if (!ostree_repo_resolve_rev (repo, from_source, FALSE, &from_resolved, error))
            goto out;
        }
      if (!ostree_repo_resolve_rev (repo, opt_to_rev, FALSE, &to_resolved, error))
        goto out;

      parambuilder = g_variant_builder_new (G_VARIANT_TYPE ("a{sv}"));
      if (opt_min_fallback_size)
        g_variant_builder_add (parambuilder, "{sv}",
                               "min-fallback-size", g_variant_new_uint32 (g_ascii_strtoull (opt_min_fallback_size, NULL, 10)));
      if (opt_max_bsdiff_size)
        g_variant_builder_add (parambuilder, "{sv}",
                               "max-bsdiff-size", g_variant_new_uint32 (g_ascii_strtoull (opt_max_bsdiff_size, NULL, 10)));
      if (opt_max_chunk_size)
        g_variant_builder_add (parambuilder, "{sv}",
                               "max-chunk-size", g_variant_new_uint32 (g_ascii_strtoull (opt_max_chunk_size, NULL, 10)));
      if (opt_disable_bsdiff)
        g_variant_builder_add (parambuilder, "{sv}",
                               "bsdiff-enabled", g_variant_new_boolean (FALSE));

      g_variant_builder_add (parambuilder, "{sv}", "verbose", g_variant_new_boolean (TRUE));

      g_print ("Generating static delta:\n");
      g_print ("  From: %s\n", from_resolved ? from_resolved : "empty");
      g_print ("  To:   %s\n", to_resolved);
      if (!ostree_repo_static_delta_generate (repo, OSTREE_STATIC_DELTA_GENERATE_OPT_MAJOR,
                                              from_resolved, to_resolved, NULL,
                                              g_variant_builder_end (parambuilder),
                                              cancellable, error))
        goto out;

    }

  ret = TRUE;
 out:
  if (context)
    g_option_context_free (context);
  return ret;
}
コード例 #19
0
ファイル: ot-builtin-checkout.c プロジェクト: giuseppe/ostree
static gboolean
process_many_checkouts (OstreeRepo         *repo,
                        GFile              *target,
                        GCancellable       *cancellable,
                        GError            **error)
{
  gboolean ret = FALSE;
  gsize len;
  GError *temp_error = NULL;
  gs_unref_object GInputStream *instream = NULL;
  gs_unref_object GDataInputStream *datastream = NULL;
  gs_free char *revision = NULL;
  gs_free char *subpath = NULL;
  gs_free char *resolved_commit = NULL;

  if (opt_from_stdin)
    {
      instream = (GInputStream*)g_unix_input_stream_new (0, FALSE);
    }
  else
    {
      gs_unref_object GFile *f = g_file_new_for_path (opt_from_file);

      instream = (GInputStream*)g_file_read (f, cancellable, error);
      if (!instream)
        goto out;
    }
    
  datastream = g_data_input_stream_new (instream);

  while ((revision = g_data_input_stream_read_upto (datastream, "", 1, &len,
                                                    cancellable, &temp_error)) != NULL)
    {
      if (revision[0] == '\0')
        break;

      /* Read the null byte */
      (void) g_data_input_stream_read_byte (datastream, cancellable, NULL);
      g_free (subpath);
      subpath = g_data_input_stream_read_upto (datastream, "", 1, &len,
                                               cancellable, &temp_error);
      if (temp_error)
        {
          g_propagate_error (error, temp_error);
          goto out;
        }

      /* Read the null byte */
      (void) g_data_input_stream_read_byte (datastream, cancellable, NULL);

      if (!ostree_repo_resolve_rev (repo, revision, FALSE, &resolved_commit, error))
        goto out;

      if (!process_one_checkout (repo, resolved_commit, subpath, target,
                                 cancellable, error))
        {
          g_prefix_error (error, "Processing tree %s: ", resolved_commit);
          goto out;
        }

      g_free (revision);
    }
  if (temp_error)
    {
      g_propagate_error (error, temp_error);
      goto out;
    }

  ret = TRUE;
 out:
  return ret;
}
コード例 #20
0
gboolean
rpmostree_commit (GFile         *rootfs,
                  OstreeRepo    *repo,
                  const char    *refname,
                  GVariant      *metadata,
                  const char    *gpg_keyid,
                  gboolean       enable_selinux,
                  GCancellable  *cancellable,
                  GError       **error)
{
    gboolean ret = FALSE;
    gs_unref_object OstreeMutableTree *mtree = NULL;
    OstreeRepoCommitModifier *commit_modifier = NULL;
    gs_free char *parent_revision = NULL;
    gs_free char *new_revision = NULL;
    gs_unref_object GFile *root_tree = NULL;
    gs_unref_object OstreeSePolicy *sepolicy = NULL;
    gs_fd_close int rootfs_fd = -1;

    if (!gs_file_open_dir_fd (rootfs, &rootfs_fd, cancellable, error))
        goto out;

    /* hardcode targeted policy for now */
    if (enable_selinux)
    {
        if (!rpmostree_prepare_rootfs_get_sepolicy (rootfs_fd, ".", &sepolicy, cancellable, error))
            goto out;
    }

    g_print ("Committing '%s' ...\n", gs_file_get_path_cached (rootfs));
    if (!ostree_repo_prepare_transaction (repo, NULL, cancellable, error))
        goto out;

    mtree = ostree_mutable_tree_new ();
    commit_modifier = ostree_repo_commit_modifier_new (0, NULL, NULL, NULL);
    ostree_repo_commit_modifier_set_xattr_callback (commit_modifier,
            read_xattrs_cb, NULL,
            GINT_TO_POINTER (rootfs_fd));
    if (sepolicy)
    {
        const char *policy_name = ostree_sepolicy_get_name (sepolicy);
        g_print ("Labeling with SELinux policy '%s'\n", policy_name);
        ostree_repo_commit_modifier_set_sepolicy (commit_modifier, sepolicy);
    }

    if (!ostree_repo_write_directory_to_mtree (repo, rootfs, mtree, commit_modifier, cancellable, error))
        goto out;
    if (!ostree_repo_write_mtree (repo, mtree, &root_tree, cancellable, error))
        goto out;

    if (!ostree_repo_resolve_rev (repo, refname, TRUE, &parent_revision, error))
        goto out;

    if (!ostree_repo_write_commit (repo, parent_revision, "", "", metadata,
                                   (OstreeRepoFile*)root_tree, &new_revision,
                                   cancellable, error))
        goto out;

    if (gpg_keyid)
    {
        g_print ("Signing commit %s with key %s\n", new_revision, gpg_keyid);
        if (!ostree_repo_sign_commit (repo, new_revision, gpg_keyid, NULL,
                                      cancellable, error))
            goto out;
    }

    ostree_repo_transaction_set_ref (repo, NULL, refname, new_revision);

    if (!ostree_repo_commit_transaction (repo, NULL, cancellable, error))
        goto out;

    g_print ("%s => %s\n", refname, new_revision);

    if (!g_getenv ("RPM_OSTREE_PRESERVE_ROOTFS"))
        (void) gs_shutil_rm_rf (rootfs, NULL, NULL);
    else
        g_print ("Preserved %s\n", gs_file_get_path_cached (rootfs));

    ret = TRUE;
out:
    return ret;
}
コード例 #21
0
ファイル: ostree-repo-pull.c プロジェクト: dnarvaez/ostree
gboolean
ostree_repo_pull (OstreeRepo               *self,
                  const char               *remote_name,
                  char                    **refs_to_fetch,
                  OstreeRepoPullFlags       flags,
                  OstreeAsyncProgress      *progress,
                  GCancellable             *cancellable,
                  GError                  **error)
{
  gboolean ret = FALSE;
  GHashTableIter hash_iter;
  gpointer key, value;
  gboolean tls_permissive = FALSE;
  OstreeFetcherConfigFlags fetcher_flags = 0;
  gs_free char *remote_key = NULL;
  gs_free char *path = NULL;
  gs_free char *baseurl = NULL;
  gs_free char *summary_data = NULL;
  gs_unref_hashtable GHashTable *requested_refs_to_fetch = NULL;
  gs_unref_hashtable GHashTable *updated_refs = NULL;
  gs_unref_hashtable GHashTable *commits_to_fetch = NULL;
  gs_free char *remote_mode_str = NULL;
  GSource *queue_src = NULL;
  OtPullData pull_data_real = { 0, };
  OtPullData *pull_data = &pull_data_real;
  SoupURI *summary_uri = NULL;
  GKeyFile *config = NULL;
  GKeyFile *remote_config = NULL;
  char **configured_branches = NULL;
  guint64 bytes_transferred;
  guint64 start_time;
  guint64 end_time;

  pull_data->async_error = error;
  pull_data->main_context = g_main_context_ref_thread_default ();
  pull_data->loop = g_main_loop_new (pull_data->main_context, FALSE);
  pull_data->flags = flags;

  pull_data->repo = self;
  pull_data->progress = progress;

  pull_data->scanned_metadata = g_hash_table_new_full (ostree_hash_object_name, g_variant_equal,
                                                       (GDestroyNotify)g_variant_unref, NULL);
  pull_data->requested_content = g_hash_table_new_full (g_str_hash, g_str_equal,
                                                        (GDestroyNotify)g_free, NULL);
  pull_data->requested_metadata = g_hash_table_new_full (g_str_hash, g_str_equal,
                                                         (GDestroyNotify)g_free, NULL);

  start_time = g_get_monotonic_time ();

  pull_data->remote_name = g_strdup (remote_name);
  config = ostree_repo_get_config (self);

  remote_key = g_strdup_printf ("remote \"%s\"", pull_data->remote_name);
  if (!repo_get_string_key_inherit (self, remote_key, "url", &baseurl, error))
    goto out;
  pull_data->base_uri = soup_uri_new (baseurl);

#ifdef HAVE_GPGME
  if (!ot_keyfile_get_boolean_with_default (config, remote_key, "gpg-verify",
                                            TRUE, &pull_data->gpg_verify, error))
    goto out;
#else
  pull_data->gpg_verify = FALSE;
#endif

  if (!ot_keyfile_get_boolean_with_default (config, remote_key, "tls-permissive",
                                            FALSE, &tls_permissive, error))
    goto out;
  if (tls_permissive)
    fetcher_flags |= OSTREE_FETCHER_FLAGS_TLS_PERMISSIVE;

  pull_data->fetcher = ostree_fetcher_new (pull_data->repo->tmp_dir,
                                           fetcher_flags);

  if (!pull_data->base_uri)
    {
      g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
                   "Failed to parse url '%s'", baseurl);
      goto out;
    }

  if (!load_remote_repo_config (pull_data, &remote_config, cancellable, error))
    goto out;

  if (!ot_keyfile_get_value_with_default (remote_config, "core", "mode", "bare",
                                          &remote_mode_str, error))
    goto out;

  if (!ostree_repo_mode_from_string (remote_mode_str, &pull_data->remote_mode, error))
    goto out;

  if (pull_data->remote_mode != OSTREE_REPO_MODE_ARCHIVE_Z2)
    {
      g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
                   "Can't pull from archives with mode \"%s\"",
                   remote_mode_str);
      goto out;
    }

  requested_refs_to_fetch = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
  updated_refs = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
  commits_to_fetch = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);

  if (refs_to_fetch != NULL)
    {
      char **strviter;
      for (strviter = refs_to_fetch; *strviter; strviter++)
        {
          const char *branch = *strviter;
          char *contents;

          if (ostree_validate_checksum_string (branch, NULL))
            {
              char *key = g_strdup (branch);
              g_hash_table_insert (commits_to_fetch, key, key);
            }
          else
            {
              if (!fetch_ref_contents (pull_data, branch, &contents, cancellable, error))
                goto out;
      
              /* Transfer ownership of contents */
              g_hash_table_insert (requested_refs_to_fetch, g_strdup (branch), contents);
            }
        }
    }
  else
    {
      GError *temp_error = NULL;
      gboolean fetch_all_refs;

      configured_branches = g_key_file_get_string_list (config, remote_key, "branches", NULL, &temp_error);
      if (configured_branches == NULL && temp_error != NULL)
        {
          if (g_error_matches (temp_error, G_KEY_FILE_ERROR, G_KEY_FILE_ERROR_KEY_NOT_FOUND))
            {
              g_clear_error (&temp_error);
              fetch_all_refs = TRUE;
            }
          else
            {
              g_propagate_error (error, temp_error);
              goto out;
            }
        }
      else
        fetch_all_refs = FALSE;

      if (fetch_all_refs)
        {
          summary_uri = soup_uri_copy (pull_data->base_uri);
          path = g_build_filename (soup_uri_get_path (summary_uri), "refs", "summary", NULL);
          soup_uri_set_path (summary_uri, path);
          
          if (!fetch_uri_contents_utf8_sync (pull_data, summary_uri, &summary_data, cancellable, error))
            goto out;
          
          if (!parse_ref_summary (summary_data, &requested_refs_to_fetch, error))
            goto out;
        }
      else
        {
          char **branches_iter = configured_branches;

          if (!(branches_iter && *branches_iter))
            g_print ("No configured branches for remote %s\n", pull_data->remote_name);
          for (;branches_iter && *branches_iter; branches_iter++)
            {
              const char *branch = *branches_iter;
              char *contents;
              
              if (!fetch_ref_contents (pull_data, branch, &contents, cancellable, error))
                goto out;
              
              /* Transfer ownership of contents */
              g_hash_table_insert (requested_refs_to_fetch, g_strdup (branch), contents);
            }
        }
    }

  if (!ostree_repo_prepare_transaction (pull_data->repo, &pull_data->transaction_resuming,
                                        cancellable, error))
    goto out;

  pull_data->metadata_objects_to_fetch = ot_waitable_queue_new ();
  pull_data->metadata_objects_to_scan = ot_waitable_queue_new ();
  pull_data->metadata_thread = g_thread_new ("metadatascan", metadata_thread_main, pull_data);

  g_hash_table_iter_init (&hash_iter, commits_to_fetch);
  while (g_hash_table_iter_next (&hash_iter, &key, &value))
    {
      const char *commit = value;

      ot_waitable_queue_push (pull_data->metadata_objects_to_scan,
                              pull_worker_message_new (PULL_MSG_SCAN,
                                                       ostree_object_name_serialize (commit, OSTREE_OBJECT_TYPE_COMMIT)));
    }

  g_hash_table_iter_init (&hash_iter, requested_refs_to_fetch);
  while (g_hash_table_iter_next (&hash_iter, &key, &value))
    {
      const char *ref = key;
      const char *sha256 = value;

      ot_waitable_queue_push (pull_data->metadata_objects_to_scan,
                              pull_worker_message_new (PULL_MSG_SCAN,
                                                       ostree_object_name_serialize (sha256, OSTREE_OBJECT_TYPE_COMMIT)));
      g_hash_table_insert (updated_refs, g_strdup (ref), g_strdup (sha256));
    }
  
  {
    queue_src = ot_waitable_queue_create_source (pull_data->metadata_objects_to_fetch);
    g_source_set_callback (queue_src, (GSourceFunc)on_metadata_objects_to_fetch_ready, pull_data, NULL);
    g_source_attach (queue_src, pull_data->main_context);
    g_source_unref (queue_src);
  }

  /* Prime the message queue */
  pull_data->idle_serial++;
  ot_waitable_queue_push (pull_data->metadata_objects_to_scan,
                          pull_worker_message_new (PULL_MSG_MAIN_IDLE, GUINT_TO_POINTER (pull_data->idle_serial)));
  
  /* Now await work completion */
  if (!run_mainloop_monitor_fetcher (pull_data))
    goto out;
  

  g_hash_table_iter_init (&hash_iter, updated_refs);
  while (g_hash_table_iter_next (&hash_iter, &key, &value))
    {
      const char *ref = key;
      const char *checksum = value;
      gs_free char *remote_ref = NULL;
      gs_free char *original_rev = NULL;
          
      remote_ref = g_strdup_printf ("%s/%s", pull_data->remote_name, ref);

      if (!ostree_repo_resolve_rev (pull_data->repo, remote_ref, TRUE, &original_rev, error))
        goto out;
          
      if (original_rev && strcmp (checksum, original_rev) == 0)
        {
          g_print ("remote %s is unchanged from %s\n", remote_ref, original_rev);
        }
      else
        {
          ostree_repo_transaction_set_ref (pull_data->repo, pull_data->remote_name, ref, checksum);

          g_print ("remote %s is now %s\n", remote_ref, checksum);
        }
    }

  if (!ostree_repo_commit_transaction (pull_data->repo, NULL, cancellable, error))
    goto out;

  end_time = g_get_monotonic_time ();

  bytes_transferred = ostree_fetcher_bytes_transferred (pull_data->fetcher);
  if (bytes_transferred > 0)
    {
      guint shift; 
      if (bytes_transferred < 1024)
        shift = 1;
      else
        shift = 1024;
      g_print ("%u metadata, %u content objects fetched; %" G_GUINT64_FORMAT " %s transferred in %u seconds\n", 
               pull_data->n_fetched_metadata, pull_data->n_fetched_content,
               (guint64)(bytes_transferred / shift),
               shift == 1 ? "B" : "KiB",
               (guint) ((end_time - start_time) / G_USEC_PER_SEC));
    }

  ret = TRUE;
 out:
  if (pull_data->main_context)
    g_main_context_unref (pull_data->main_context);
  if (pull_data->loop)
    g_main_loop_unref (pull_data->loop);
  g_strfreev (configured_branches);
  g_clear_object (&pull_data->fetcher);
  g_free (pull_data->remote_name);
  if (pull_data->base_uri)
    soup_uri_free (pull_data->base_uri);
  if (queue_src)
    g_source_destroy (queue_src);
  if (pull_data->metadata_thread)
    {
      ot_waitable_queue_push (pull_data->metadata_objects_to_scan,
                              pull_worker_message_new (PULL_MSG_QUIT, NULL));
      g_thread_join (pull_data->metadata_thread);
    }
  g_clear_pointer (&pull_data->metadata_objects_to_scan, (GDestroyNotify) ot_waitable_queue_unref);
  g_clear_pointer (&pull_data->metadata_objects_to_fetch, (GDestroyNotify) ot_waitable_queue_unref);
  g_clear_pointer (&pull_data->scanned_metadata, (GDestroyNotify) g_hash_table_unref);
  g_clear_pointer (&pull_data->requested_content, (GDestroyNotify) g_hash_table_unref);
  g_clear_pointer (&pull_data->requested_metadata, (GDestroyNotify) g_hash_table_unref);
  g_clear_pointer (&remote_config, (GDestroyNotify) g_key_file_unref);
  if (summary_uri)
    soup_uri_free (summary_uri);
  return ret;
}
コード例 #22
0
ファイル: ot-builtin-commit.c プロジェクト: cgwalters/ostree
gboolean
ostree_builtin_commit (int argc, char **argv, GCancellable *cancellable, GError **error)
{
  GOptionContext *context;
  gs_unref_object OstreeRepo *repo = NULL;
  gboolean ret = FALSE;
  gboolean skip_commit = FALSE;
  gs_unref_object GFile *arg = NULL;
  gs_free char *parent = NULL;
  gs_free char *commit_checksum = NULL;
  gs_unref_object GFile *root = NULL;
  gs_unref_variant GVariant *metadata = NULL;
  gs_unref_variant GVariant *detached_metadata = NULL;
  gs_unref_object OstreeMutableTree *mtree = NULL;
  gs_free char *tree_type = NULL;
  gs_unref_hashtable GHashTable *mode_adds = NULL;
  OstreeRepoCommitModifierFlags flags = 0;
  OstreeRepoCommitModifier *modifier = NULL;
  OstreeRepoTransactionStats stats;

  context = g_option_context_new ("[PATH] - Commit a new revision");

  if (!ostree_option_context_parse (context, options, &argc, &argv, OSTREE_BUILTIN_FLAG_NONE, &repo, cancellable, error))
    goto out;

  if (opt_statoverride_file)
    {
      if (!parse_statoverride_file (&mode_adds, cancellable, error))
        goto out;
    }

  if (opt_metadata_strings)
    {
      if (!parse_keyvalue_strings (opt_metadata_strings,
                                   &metadata, error))
        goto out;
    }
  if (opt_detached_metadata_strings)
    {
      if (!parse_keyvalue_strings (opt_detached_metadata_strings,
                                   &detached_metadata, error))
        goto out;
    }
      
  if (!opt_branch)
    {
      g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED,
                           "A branch must be specified with --branch");
      goto out;
    }

  if (opt_no_xattrs)
    flags |= OSTREE_REPO_COMMIT_MODIFIER_FLAGS_SKIP_XATTRS;
  if (opt_generate_sizes)
    flags |= OSTREE_REPO_COMMIT_MODIFIER_FLAGS_GENERATE_SIZES;
  if (opt_disable_fsync)
    ostree_repo_set_disable_fsync (repo, TRUE);

  if (flags != 0
      || opt_owner_uid >= 0
      || opt_owner_gid >= 0
      || opt_statoverride_file != NULL
      || opt_no_xattrs)
    {
      modifier = ostree_repo_commit_modifier_new (flags, commit_filter, mode_adds, NULL);
    }

  if (!ostree_repo_resolve_rev (repo, opt_branch, TRUE, &parent, error))
    goto out;

  if (!opt_subject && !opt_body)
    {
      if (!commit_editor (repo, opt_branch, &opt_subject, &opt_body, cancellable, error))
        goto out;
    }

  if (!opt_subject)
    {
      g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED,
                           "A subject must be specified with --subject");
      goto out;
    }

  if (!ostree_repo_prepare_transaction (repo, NULL, cancellable, error))
    goto out;

  if (opt_link_checkout_speedup && !ostree_repo_scan_hardlinks (repo, cancellable, error))
    goto out;

  mtree = ostree_mutable_tree_new ();

  if (argc <= 1 && (opt_trees == NULL || opt_trees[0] == NULL))
    {
      char *current_dir = g_get_current_dir ();
      arg = g_file_new_for_path (current_dir);
      g_free (current_dir);

      if (!ostree_repo_write_directory_to_mtree (repo, arg, mtree, modifier,
                                                 cancellable, error))
        goto out;
    }
  else if (opt_trees != NULL)
    {
      const char *const*tree_iter;
      const char *tree;
      const char *eq;

      for (tree_iter = (const char *const*)opt_trees; *tree_iter; tree_iter++)
        {
          tree = *tree_iter;

          eq = strchr (tree, '=');
          if (!eq)
            {
              g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
                           "Missing type in tree specification '%s'", tree);
              goto out;
            }
          g_free (tree_type);
          tree_type = g_strndup (tree, eq - tree);
          tree = eq + 1;

          g_clear_object (&arg);
          if (strcmp (tree_type, "dir") == 0)
            {
              arg = g_file_new_for_path (tree);
              if (!ostree_repo_write_directory_to_mtree (repo, arg, mtree, modifier,
                                                         cancellable, error))
                goto out;
            }
          else if (strcmp (tree_type, "tar") == 0)
            {
              arg = g_file_new_for_path (tree);
              if (!ostree_repo_write_archive_to_mtree (repo, arg, mtree, modifier,
                                                       opt_tar_autocreate_parents,
                                                       cancellable, error))
                goto out;
            }
          else if (strcmp (tree_type, "ref") == 0)
            {
              if (!ostree_repo_read_commit (repo, tree, &arg, NULL, cancellable, error))
                goto out;

              if (!ostree_repo_write_directory_to_mtree (repo, arg, mtree, modifier,
                                                         cancellable, error))
                goto out;
            }
          else
            {
              g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
                           "Invalid tree type specification '%s'", tree_type);
              goto out;
            }
        }
    }
  else
    {
      g_assert (argc > 1);
      arg = g_file_new_for_path (argv[1]);
      if (!ostree_repo_write_directory_to_mtree (repo, arg, mtree, modifier,
                                                 cancellable, error))
        goto out;
    }

  if (mode_adds && g_hash_table_size (mode_adds) > 0)
    {
      GHashTableIter hash_iter;
      gpointer key, value;

      g_hash_table_iter_init (&hash_iter, mode_adds);

      while (g_hash_table_iter_next (&hash_iter, &key, &value))
        {
          g_printerr ("Unmatched statoverride path: %s\n", (char*)key);
        }
      g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
                   "Unmatched statoverride paths");
      goto out;
    }

  if (!ostree_repo_write_mtree (repo, mtree, &root, cancellable, error))
    goto out;

  if (opt_skip_if_unchanged && parent)
    {
      gs_unref_object GFile *parent_root;

      if (!ostree_repo_read_commit (repo, parent, &parent_root, NULL, cancellable, error))
        goto out;

      if (g_file_equal (root, parent_root))
        skip_commit = TRUE;
    }

  if (!skip_commit)
    {
      if (!ostree_repo_write_commit (repo, parent, opt_subject, opt_body, metadata,
                                     OSTREE_REPO_FILE (root),
                                     &commit_checksum, cancellable, error))
        goto out;

      if (detached_metadata)
        {
          if (!ostree_repo_write_commit_detached_metadata (repo, commit_checksum,
                                                           detached_metadata,
                                                           cancellable, error))
            goto out;
        }

#ifdef HAVE_GPGME
      if (opt_key_ids)
        {
          char **iter;

          for (iter = opt_key_ids; iter && *iter; iter++)
            {
              const char *keyid = *iter;

              if (!ostree_repo_sign_commit (repo,
                                            commit_checksum,
                                            keyid,
                                            opt_gpg_homedir,
                                            cancellable,
                                            error))
                goto out;
            }
        }
#endif

      ostree_repo_transaction_set_ref (repo, NULL, opt_branch, commit_checksum);

      if (!ostree_repo_commit_transaction (repo, &stats, cancellable, error))
        goto out;
    }
  else
    {
      commit_checksum = g_strdup (parent);
    }

  if (opt_table_output)
    {
      g_print ("Commit: %s\n", commit_checksum);
      g_print ("Metadata Total: %u\n", stats.metadata_objects_total);
      g_print ("Metadata Written: %u\n", stats.metadata_objects_written);
      g_print ("Content Total: %u\n", stats.content_objects_total);
      g_print ("Content Written: %u\n", stats.content_objects_written);
      g_print ("Content Bytes Written: %" G_GUINT64_FORMAT "\n", stats.content_bytes_written);
    }
  else
    {
      g_print ("%s\n", commit_checksum);
    }

  ret = TRUE;
 out:
  if (repo)
    ostree_repo_abort_transaction (repo, cancellable, NULL);
  if (context)
    g_option_context_free (context);
  if (modifier)
    ostree_repo_commit_modifier_unref (modifier);
  return ret;
}
コード例 #23
0
ファイル: ot-builtin-fsck.c プロジェクト: endlessm/ostree
gboolean
ostree_builtin_fsck (int argc, char **argv, OstreeCommandInvocation *invocation, GCancellable *cancellable, GError **error)
{
  g_autoptr(OstreeRepo) repo = NULL;
  gboolean found_corruption = FALSE;

  g_autoptr(GOptionContext) context = g_option_context_new ("");
  if (!ostree_option_context_parse (context, options, &argc, &argv, invocation, &repo, cancellable, error))
    return FALSE;

  if (!opt_quiet)
    g_print ("Validating refs...\n");

  /* Validate that the commit for each ref is available */
  g_autoptr(GHashTable) all_refs = NULL;
  if (!ostree_repo_list_refs (repo, NULL, &all_refs,
                              cancellable, error))
    return FALSE;

  GHashTableIter hash_iter;
  gpointer key, value;
  g_hash_table_iter_init (&hash_iter, all_refs);
  while (g_hash_table_iter_next (&hash_iter, &key, &value))
    {
      const char *refspec = key;
      const char *checksum = value;
      g_autofree char *ref_name = NULL;
      if (!ostree_parse_refspec (refspec, NULL, &ref_name, error))
        return FALSE;
      if (!fsck_commit_for_ref (repo, checksum, NULL, ref_name,
                                &found_corruption, cancellable, error))
        return FALSE;
    }

  if (!opt_quiet)
    g_print ("Validating refs in collections...\n");

  g_autoptr(GHashTable) all_collection_refs = NULL;  /* (element-type OstreeCollectionRef utf8) */
  if (!ostree_repo_list_collection_refs (repo, NULL, &all_collection_refs,
                                         OSTREE_REPO_LIST_REFS_EXT_EXCLUDE_REMOTES,
                                         cancellable, error))
    return FALSE;

  g_hash_table_iter_init (&hash_iter, all_collection_refs);
  while (g_hash_table_iter_next (&hash_iter, &key, &value))
    {
      const OstreeCollectionRef *ref = key;
      if (!fsck_commit_for_ref (repo, value, ref->collection_id, ref->ref_name,
                                &found_corruption, cancellable, error))
        return FALSE;
    }

  if (!opt_quiet)
    g_print ("Enumerating objects...\n");

  g_autoptr(GHashTable) objects = NULL;
  if (!ostree_repo_list_objects (repo, OSTREE_REPO_LIST_OBJECTS_ALL,
                                 &objects, cancellable, error))
    return FALSE;

  g_autoptr(GHashTable) commits = g_hash_table_new_full (ostree_hash_object_name, g_variant_equal,
                                                         (GDestroyNotify)g_variant_unref, NULL);


  g_autoptr(GPtrArray) tombstones = NULL;
  if (opt_add_tombstones)
    tombstones = g_ptr_array_new_with_free_func (g_free);

  if (opt_verify_back_refs)
    opt_verify_bindings = TRUE;

  guint n_partial = 0;
  g_hash_table_iter_init (&hash_iter, objects);
  while (g_hash_table_iter_next (&hash_iter, &key, &value))
    {
      GVariant *serialized_key = key;
      const char *checksum;
      OstreeObjectType objtype;
      OstreeRepoCommitState commitstate = 0;
      g_autoptr(GVariant) commit = NULL;

      ostree_object_name_deserialize (serialized_key, &checksum, &objtype);

      if (objtype == OSTREE_OBJECT_TYPE_COMMIT)
        {
          if (!ostree_repo_load_commit (repo, checksum, &commit, &commitstate, error))
            return FALSE;

          /* If requested, check that all the refs listed in the ref-bindings
           * for this commit resolve back to this commit. */
          if (opt_verify_back_refs)
            {
              g_autoptr(GVariant) metadata = g_variant_get_child_value (commit, 0);

              const char *collection_id = NULL;
              if (!g_variant_lookup (metadata,
                                     OSTREE_COMMIT_META_KEY_COLLECTION_BINDING,
                                     "&s",
                                     &collection_id))
                collection_id = NULL;

              g_autofree const char **refs = NULL;
              if (g_variant_lookup (metadata,
                                    OSTREE_COMMIT_META_KEY_REF_BINDING,
                                    "^a&s",
                                    &refs))
                {
                  for (const char **iter = refs; *iter != NULL; ++iter)
                    {
                      g_autofree char *checksum_for_ref = NULL;

                      if (collection_id != NULL)
                        {
                          const OstreeCollectionRef collection_ref = { (char *) collection_id, (char *) *iter };
                          if (!ostree_repo_resolve_collection_ref (repo, &collection_ref,
                                                                   TRUE,
                                                                   OSTREE_REPO_RESOLVE_REV_EXT_NONE,
                                                                   &checksum_for_ref,
                                                                   cancellable,
                                                                   error))
                            return FALSE;
                        }
                      else
                        {
                          if (!ostree_repo_resolve_rev (repo, *iter, TRUE,
                                                        &checksum_for_ref, error))
                            return FALSE;
                        }

                      if (checksum_for_ref == NULL)
                        {
                          if (collection_id != NULL)
                            return glnx_throw (error,
                                               "Collection–ref (%s, %s) in bindings for commit %s does not exist",
                                               collection_id, *iter, checksum);
                          else
                            return glnx_throw (error,
                                               "Ref ‘%s’ in bindings for commit %s does not exist",
                                               *iter, checksum);
                        }
                      else if (g_strcmp0 (checksum_for_ref, checksum) != 0)
                        {
                          if (collection_id != NULL)
                            return glnx_throw (error,
                                               "Collection–ref (%s, %s) in bindings for commit %s does not resolve to that commit",
                                               collection_id, *iter, checksum);
                          else
                            return glnx_throw (error,
                                               "Ref ‘%s’ in bindings for commit %s does not resolve to that commit",
                                               *iter, checksum);
                        }
                    }
                }
            }

          if (opt_add_tombstones)
            {
              GError *local_error = NULL;
              g_autofree char *parent = ostree_commit_get_parent (commit);
              if (parent)
                {
                  g_autoptr(GVariant) parent_commit = NULL;
                  if (!ostree_repo_load_variant (repo, OSTREE_OBJECT_TYPE_COMMIT, parent,
                                                 &parent_commit, &local_error))
                    {
                      if (g_error_matches (local_error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND))
                        {
                          g_ptr_array_add (tombstones, g_strdup (checksum));
                          g_clear_error (&local_error);
                        }
                      else
                        {
                          g_propagate_error (error, local_error);
                          return FALSE;
                        }
                    }
                }
            }

          if (commitstate & OSTREE_REPO_COMMIT_STATE_PARTIAL)
            n_partial++;
          else
            g_hash_table_add (commits, g_variant_ref (serialized_key));
        }
    }

  g_clear_pointer (&objects, (GDestroyNotify) g_hash_table_unref);

  if (!opt_quiet)
    g_print ("Verifying content integrity of %u commit objects...\n",
             (guint)g_hash_table_size (commits));

  if (!fsck_reachable_objects_from_commits (repo, commits, &found_corruption,
                                            cancellable, error))
    return FALSE;

  if (opt_add_tombstones)
    {
      guint i;
      if (tombstones->len)
        {
          if (!ot_enable_tombstone_commits (repo, error))
            return FALSE;
        }
      for (i = 0; i < tombstones->len; i++)
        {
          const char *checksum = tombstones->pdata[i];
          g_print ("Adding tombstone for commit %s\n", checksum);
          if (!ostree_repo_delete_object (repo, OSTREE_OBJECT_TYPE_COMMIT, checksum, cancellable, error))
            return FALSE;
        }
    }
  else if (n_partial > 0)
    {
      g_print ("%u partial commits not verified\n", n_partial);
    }

  if (found_corruption)
    return glnx_throw (error, "Repository corruption encountered");

  return TRUE;
}
コード例 #24
0
gboolean
flatpak_builtin_build_commit_from (int argc, char **argv, GCancellable *cancellable, GError **error)
{
  g_autoptr(GOptionContext) context = NULL;
  g_autoptr(GFile) dst_repofile = NULL;
  g_autoptr(OstreeRepo) dst_repo = NULL;
  g_autoptr(GFile) src_repofile = NULL;
  g_autoptr(OstreeRepo) src_repo = NULL;
  g_autofree char *src_repo_uri = NULL;
  const char *dst_repo_arg;
  const char **dst_refs;
  int n_dst_refs = 0;
  g_autoptr(FlatpakRepoTransaction) transaction = NULL;
  g_autoptr(GPtrArray) src_refs = NULL;
  g_autoptr(GPtrArray) resolved_src_refs = NULL;
  OstreeRepoCommitState src_commit_state;
  struct timespec ts;
  guint64 timestamp;
  int i;

  context = g_option_context_new (_("DST-REPO [DST-REF…] - Make a new commit from existing commits"));
  g_option_context_set_translation_domain (context, GETTEXT_PACKAGE);

  if (!flatpak_option_context_parse (context, options, &argc, &argv, FLATPAK_BUILTIN_FLAG_NO_DIR, NULL, cancellable, error))
    return FALSE;

  if (argc < 2)
    return usage_error (context, _("DST-REPO must be specified"), error);

  dst_repo_arg = argv[1];

  dst_refs = (const char **) argv + 2;
  n_dst_refs = argc - 2;

  if (opt_src_repo == NULL && n_dst_refs != 1)
    return usage_error (context, _("If --src-repo is not specified, exactly one destination ref must be specified"), error);

  if (opt_src_ref != NULL && n_dst_refs != 1)
    return usage_error (context, _("If --src-ref is specified, exactly one destination ref must be specified"), error);

  if (opt_src_repo == NULL && opt_src_ref == NULL)
    return flatpak_fail (error, _("Either --src-repo or --src-ref must be specified."));

  /* Always create a commit if we're eol:ing, even though the app is the same */
  if (opt_endoflife != NULL || opt_endoflife_rebase != NULL)
    opt_force = TRUE;

  if (opt_endoflife_rebase)
    {
      opt_endoflife_rebase_new = g_new0 (char *, g_strv_length (opt_endoflife_rebase));

      for (i = 0; opt_endoflife_rebase[i] != NULL; i++)
        {
          char *rebase_old = opt_endoflife_rebase[i];
          char *rebase_new = strchr (rebase_old, '=');

          if (rebase_new == NULL) {
            return usage_error (context, _("Invalid argument format of use  --end-of-life-rebase=OLDID=NEWID"), error);
          }
          *rebase_new = 0;
          rebase_new++;

          if (!flatpak_is_valid_name (rebase_old, error))
            return glnx_prefix_error (error, _("Invalid name %s in --end-of-life-rebase"), rebase_old);

          if (!flatpak_is_valid_name (rebase_new, error))
            return glnx_prefix_error (error, _("Invalid name %s in --end-of-life-rebase"), rebase_new);

          opt_endoflife_rebase_new[i] = rebase_new;
        }
    }

  if (opt_timestamp)
    {
      if (!parse_datetime (&ts, opt_timestamp, NULL))
        return flatpak_fail (error, _("Could not parse '%s'"), opt_timestamp);
    }

  dst_repofile = g_file_new_for_commandline_arg (dst_repo_arg);
  if (!g_file_query_exists (dst_repofile, cancellable))
    return flatpak_fail (error, _("'%s' is not a valid repository"), dst_repo_arg);

  dst_repo = ostree_repo_new (dst_repofile);
  if (!ostree_repo_open (dst_repo, cancellable, error))
    return FALSE;

  if (opt_disable_fsync)
    ostree_repo_set_disable_fsync (dst_repo, TRUE);

  if (opt_src_repo)
    {
      src_repofile = g_file_new_for_commandline_arg (opt_src_repo);
      if (!g_file_query_exists (src_repofile, cancellable))
        return flatpak_fail (error, _("'%s' is not a valid repository"), opt_src_repo);

      src_repo_uri = g_file_get_uri (src_repofile);
      src_repo = ostree_repo_new (src_repofile);
      if (!ostree_repo_open (src_repo, cancellable, error))
        return FALSE;
    }
  else
    {
      src_repo = g_object_ref (dst_repo);
    }

  src_refs = g_ptr_array_new_with_free_func (g_free);
  if (opt_src_ref)
    {
      g_assert (n_dst_refs == 1);
      g_ptr_array_add (src_refs, g_strdup (opt_src_ref));
    }
  else
    {
      g_assert (opt_src_repo != NULL);
      if (n_dst_refs == 0)
        {
          g_autofree const char **keys = NULL;
          g_autoptr(GHashTable) all_src_refs = NULL;

          if (!ostree_repo_list_refs (src_repo, NULL, &all_src_refs,
                                      cancellable, error))
            return FALSE;

          keys = (const char **) g_hash_table_get_keys_as_array (all_src_refs, NULL);

          for (i = 0; keys[i] != NULL; i++)
            {
              if (g_str_has_prefix (keys[i], "runtime/") ||
                  g_str_has_prefix (keys[i], "app/"))
                g_ptr_array_add (src_refs, g_strdup (keys[i]));
            }
          n_dst_refs = src_refs->len;
          dst_refs = (const char **) src_refs->pdata;
        }
      else
        {
          for (i = 0; i < n_dst_refs; i++)
            g_ptr_array_add (src_refs, g_strdup (dst_refs[i]));
        }
    }

  resolved_src_refs = g_ptr_array_new_with_free_func (g_free);
  for (i = 0; i < src_refs->len; i++)
    {
      const char *src_ref = g_ptr_array_index (src_refs, i);
      char *resolved_ref;

      if (!ostree_repo_resolve_rev (src_repo, src_ref, FALSE, &resolved_ref, error))
        return FALSE;

      g_ptr_array_add (resolved_src_refs, resolved_ref);
    }

  if (src_repo_uri != NULL)
    {
      OstreeRepoPullFlags pullflags = 0;
      GVariantBuilder builder;
      g_autoptr(OstreeAsyncProgress) progress = NULL;
      g_auto(GLnxConsoleRef) console = { 0, };
      g_autoptr(GVariant) options = NULL;
      gboolean res;

      if (opt_untrusted)
        pullflags |= OSTREE_REPO_PULL_FLAGS_UNTRUSTED;

      glnx_console_lock (&console);
      if (console.is_tty)
        progress = ostree_async_progress_new_and_connect (ostree_repo_pull_default_console_progress_changed, &console);

      g_variant_builder_init (&builder, G_VARIANT_TYPE ("a{sv}"));
      g_variant_builder_add (&builder, "{s@v}", "flags",
                             g_variant_new_variant (g_variant_new_int32 (pullflags)));
      g_variant_builder_add (&builder, "{s@v}", "refs",
                             g_variant_new_variant (g_variant_new_strv ((const char * const *) resolved_src_refs->pdata,
                                                                        resolved_src_refs->len)));
      g_variant_builder_add (&builder, "{s@v}", "depth",
                             g_variant_new_variant (g_variant_new_int32 (0)));

      options = g_variant_ref_sink (g_variant_builder_end (&builder));
      res = ostree_repo_pull_with_options (dst_repo, src_repo_uri,
                                           options,
                                           progress,
                                           cancellable, error);

      if (progress)
        ostree_async_progress_finish (progress);

      if (!res)
        return FALSE;
    }

  /* By now we have the commit with commit_id==resolved_ref and dependencies in dst_repo. We now create a new
   * commit based on the toplevel tree ref from that commit.
   * This is equivalent to:
   *   ostree commit --skip-if-unchanged --repo=${destrepo} --tree=ref=${resolved_ref}
   */

  transaction = flatpak_repo_transaction_start (dst_repo, cancellable, error);
  if (transaction == NULL)
    return FALSE;

  for (i = 0; i < resolved_src_refs->len; i++)
    {
      const char *dst_ref = dst_refs[i];
      const char *resolved_ref = g_ptr_array_index (resolved_src_refs, i);
      g_autofree char *dst_parent = NULL;
      g_autoptr(GFile) dst_parent_root = NULL;
      g_autoptr(GFile) src_ref_root = NULL;
      g_autoptr(GVariant) src_commitv = NULL;
      g_autoptr(GVariant) dst_commitv = NULL;
      g_autoptr(OstreeMutableTree) mtree = NULL;
      g_autoptr(GFile) dst_root = NULL;
      g_autoptr(GVariant) commitv_metadata = NULL;
      g_autoptr(GVariant) metadata = NULL;
      const char *subject;
      const char *body;
      g_autofree char *commit_checksum = NULL;
      GVariantBuilder metadata_builder;
      gint j;
      const char *dst_collection_id = NULL;
      const char *main_collection_id = NULL;
      g_autoptr(GPtrArray) collection_ids = NULL;

      if (!ostree_repo_resolve_rev (dst_repo, dst_ref, TRUE, &dst_parent, error))
        return FALSE;

      if (dst_parent != NULL &&
          !ostree_repo_read_commit (dst_repo, dst_parent, &dst_parent_root, NULL, cancellable, error))
        return FALSE;

      if (!ostree_repo_read_commit (dst_repo, resolved_ref, &src_ref_root, NULL, cancellable, error))
        return FALSE;

      if (!ostree_repo_load_commit (dst_repo, resolved_ref, &src_commitv, &src_commit_state, error))
        return FALSE;

      if (src_commit_state & OSTREE_REPO_COMMIT_STATE_PARTIAL)
        return flatpak_fail (error, _("Can't commit from partial source commit."));

      /* Don't create a new commit if this is the same tree */
      if (!opt_force && dst_parent_root != NULL && g_file_equal (dst_parent_root, src_ref_root))
        {
          g_print (_("%s: no change\n"), dst_ref);
          continue;
        }

      mtree = ostree_mutable_tree_new ();
      if (!ostree_repo_write_directory_to_mtree (dst_repo, src_ref_root, mtree, NULL,
                                                 cancellable, error))
        return FALSE;

      if (!ostree_repo_write_mtree (dst_repo, mtree, &dst_root, cancellable, error))
        return FALSE;

      commitv_metadata = g_variant_get_child_value (src_commitv, 0);

      g_variant_get_child (src_commitv, 3, "&s", &subject);
      if (opt_subject)
        subject = (const char *) opt_subject;
      g_variant_get_child (src_commitv, 4, "&s", &body);
      if (opt_body)
        body = (const char *) opt_body;

      dst_collection_id = ostree_repo_get_collection_id (dst_repo);

      collection_ids = g_ptr_array_new_with_free_func (g_free);
      if (dst_collection_id)
        {
          main_collection_id = dst_collection_id;
          g_ptr_array_add (collection_ids, g_strdup (dst_collection_id));
        }

      if (opt_extra_collection_ids != NULL)
        {
          for (j = 0; opt_extra_collection_ids[j] != NULL; j++)
            {
              const char *cid = opt_extra_collection_ids[j];
              if (main_collection_id == NULL)
                main_collection_id = cid; /* Fall back to first arg */

              if (g_strcmp0 (cid, dst_collection_id) != 0)
                g_ptr_array_add (collection_ids, g_strdup (cid));
            }
        }

      g_ptr_array_sort (collection_ids, (GCompareFunc) flatpak_strcmp0_ptr);

      /* Copy old metadata */
      g_variant_builder_init (&metadata_builder, G_VARIANT_TYPE ("a{sv}"));

      /* Bindings. xa.ref is deprecated but added anyway for backwards compatibility. */
      g_variant_builder_add (&metadata_builder, "{sv}", "ostree.collection-binding",
                             g_variant_new_string (main_collection_id ? main_collection_id : ""));
      if (collection_ids->len > 0)
        {
          g_autoptr(GVariantBuilder) cr_builder = g_variant_builder_new (G_VARIANT_TYPE ("a(ss)"));

          for (j = 0; j < collection_ids->len; j++)
            g_variant_builder_add (cr_builder, "(ss)", g_ptr_array_index (collection_ids, j), dst_ref);

          g_variant_builder_add (&metadata_builder, "{sv}", "ostree.collection-refs-binding",
                                 g_variant_builder_end (cr_builder));
        }
      g_variant_builder_add (&metadata_builder, "{sv}", "ostree.ref-binding",
                             g_variant_new_strv (&dst_ref, 1));
      g_variant_builder_add (&metadata_builder, "{sv}", "xa.ref", g_variant_new_string (dst_ref));

      /* Record the source commit. This is nice to have, but it also
         means the commit-from gets a different commit id, which
         avoids problems with e.g.  sharing .commitmeta files
         (signatures) */
      g_variant_builder_add (&metadata_builder, "{sv}", "xa.from_commit", g_variant_new_string (resolved_ref));

      for (j = 0; j < g_variant_n_children (commitv_metadata); j++)
        {
          g_autoptr(GVariant) child = g_variant_get_child_value (commitv_metadata, j);
          g_autoptr(GVariant) keyv = g_variant_get_child_value (child, 0);
          const char *key = g_variant_get_string (keyv, NULL);

          if (strcmp (key, "xa.ref") == 0 ||
              strcmp (key, "xa.from_commit") == 0 ||
              strcmp (key, "ostree.collection-binding") == 0 ||
              strcmp (key, "ostree.collection-refs-binding") == 0 ||
              strcmp (key, "ostree.ref-binding") == 0)
            continue;

          if (opt_endoflife &&
              strcmp (key, OSTREE_COMMIT_META_KEY_ENDOFLIFE) == 0)
            continue;

          if (opt_endoflife_rebase &&
              strcmp (key, OSTREE_COMMIT_META_KEY_ENDOFLIFE_REBASE) == 0)
            continue;

          g_variant_builder_add_value (&metadata_builder, child);
        }

      if (opt_endoflife && *opt_endoflife)
        g_variant_builder_add (&metadata_builder, "{sv}", OSTREE_COMMIT_META_KEY_ENDOFLIFE,
                               g_variant_new_string (opt_endoflife));

      if (opt_endoflife_rebase)
        {
          g_auto(GStrv) dst_ref_parts = g_strsplit (dst_ref, "/", 0);

          for (j = 0; opt_endoflife_rebase[j] != NULL; j++)
            {
              const char *old_prefix = opt_endoflife_rebase[j];

              if (flatpak_has_name_prefix (dst_ref_parts[1], old_prefix))
                {
                  g_autofree char *new_id = g_strconcat (opt_endoflife_rebase_new[j], dst_ref_parts[1] + strlen(old_prefix), NULL);
                  g_autofree char *rebased_ref = g_build_filename (dst_ref_parts[0], new_id, dst_ref_parts[2], dst_ref_parts[3], NULL);

                  g_variant_builder_add (&metadata_builder, "{sv}", OSTREE_COMMIT_META_KEY_ENDOFLIFE_REBASE,
                                         g_variant_new_string (rebased_ref));
                  break;
                }
            }
        }

      timestamp = ostree_commit_get_timestamp (src_commitv);
      if (opt_timestamp)
        timestamp = ts.tv_sec;

      metadata = g_variant_ref_sink (g_variant_builder_end (&metadata_builder));
      if (!ostree_repo_write_commit_with_time (dst_repo, dst_parent, subject, body, metadata,
                                               OSTREE_REPO_FILE (dst_root),
                                               timestamp,
                                               &commit_checksum, cancellable, error))
        return FALSE;

      g_print ("%s: %s\n", dst_ref, commit_checksum);

      if (!ostree_repo_load_commit (dst_repo, commit_checksum, &dst_commitv, NULL, error))
        return FALSE;

      /* This doesn't copy the detached metadata. I'm not sure if this is a problem.
       * The main thing there is commit signatures, and we can't copy those, as the commit hash changes.
       */

      if (opt_gpg_key_ids)
        {
          char **iter;

          for (iter = opt_gpg_key_ids; iter && *iter; iter++)
            {
              const char *keyid = *iter;
              g_autoptr(GError) my_error = NULL;

              if (!ostree_repo_sign_commit (dst_repo,
                                            commit_checksum,
                                            keyid,
                                            opt_gpg_homedir,
                                            cancellable,
                                            &my_error) &&
                  !g_error_matches (my_error, G_IO_ERROR, G_IO_ERROR_EXISTS))
                {
                  g_propagate_error (error, g_steal_pointer (&my_error));
                  return FALSE;
                }
            }
        }

      if (dst_collection_id != NULL)
        {
          OstreeCollectionRef ref = { (char *) dst_collection_id, (char *) dst_ref };
          ostree_repo_transaction_set_collection_ref (dst_repo, &ref, commit_checksum);
        }
      else
        {
          ostree_repo_transaction_set_ref (dst_repo, NULL, dst_ref, commit_checksum);
        }

      if (opt_extra_collection_ids)
        {
          for (j = 0; opt_extra_collection_ids[j] != NULL; j++)
            {
              OstreeCollectionRef ref = { (char *) opt_extra_collection_ids[j], (char *) dst_ref };
              ostree_repo_transaction_set_collection_ref (dst_repo, &ref, commit_checksum);
            }
        }

      /* Copy + Rewrite any deltas */
      {
        const char *from[2];
        gsize j, n_from = 0;

        if (dst_parent != NULL)
          from[n_from++] = dst_parent;
        from[n_from++] = NULL;

        for (j = 0; j < n_from; j++)
          {
            g_autoptr(GError) local_error = NULL;
            if (!rewrite_delta (src_repo, resolved_ref, dst_repo, commit_checksum, dst_commitv, from[j], &local_error))
              g_debug ("Failed to copy delta: %s", local_error->message);
          }
      }
    }

  if (!ostree_repo_commit_transaction (dst_repo, NULL, cancellable, error))
    return FALSE;

  if (opt_update_appstream &&
      !flatpak_repo_generate_appstream (dst_repo, (const char **) opt_gpg_key_ids, opt_gpg_homedir, 0, cancellable, error))
    return FALSE;

  if (!opt_no_update_summary &&
      !flatpak_repo_update (dst_repo,
                            (const char **) opt_gpg_key_ids,
                            opt_gpg_homedir,
                            cancellable,
                            error))
    return FALSE;

  return TRUE;
}