gboolean
ostree_builtin_checksum (int argc, char **argv, GCancellable *cancellable, GError **error)
{
  g_autoptr(GOptionContext) context = NULL;
  gboolean ret = FALSE;
  g_autoptr(GFile) f = NULL;
  AsyncChecksumData data = { 0, };

  context = g_option_context_new ("PATH - Checksum a file or directory");

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

  if (argc > 1)
    f = g_file_new_for_path (argv[1]);
  else
    {
      g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED,
                           "A filename must be given");
      goto out;
    }

  data.loop = g_main_loop_new (NULL, FALSE);
  data.error = error;
  ostree_checksum_file_async (f, OSTREE_OBJECT_TYPE_FILE, G_PRIORITY_DEFAULT, cancellable, on_checksum_received, &data);
  
  g_main_loop_run (data.loop);

  ret = TRUE;
 out:
  if (data.loop)
    g_main_loop_unref (data.loop);
  return ret;
}
gboolean
ot_remote_builtin_delete (int argc, char **argv, GCancellable *cancellable, GError **error)
{
  GOptionContext *context;
  glnx_unref_object OstreeRepo *repo = NULL;
  const char *remote_name;
  gboolean ret = FALSE;

  context = g_option_context_new ("NAME - Delete a remote repository");

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

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

  remote_name = argv[1];

  if (!ostree_repo_remote_change (repo, NULL,
                                  opt_if_exists ? OSTREE_REPO_REMOTE_CHANGE_DELETE_IF_EXISTS : 
                                  OSTREE_REPO_REMOTE_CHANGE_DELETE,
                                  remote_name, NULL, NULL,
                                  cancellable, error))
    goto out;

  ret = TRUE;
 out:
  g_option_context_free (context);

  return ret;
}
gboolean
ot_remote_builtin_delete_cookie (int argc, char **argv, GCancellable *cancellable, GError **error)
{
  g_autoptr(OstreeRepo) repo = NULL;
  g_autoptr(GOptionContext) context = g_option_context_new ("NAME DOMAIN PATH COOKIE_NAME- Remote one cookie from remote");

  if (!ostree_option_context_parse (context, option_entries, &argc, &argv,
                                    OSTREE_BUILTIN_FLAG_NONE, &repo, cancellable, error))
    return FALSE;

  if (argc < 5)
    {
      ot_util_usage_error (context, "NAME, DOMAIN, PATH and COOKIE_NAME must be specified", error);
      return FALSE;
    }

  const char *remote_name = argv[1];
  const char *domain = argv[2];
  const char *path = argv[3];
  const char *cookie_name = argv[4];
  g_autofree char *cookie_file = g_strdup_printf ("%s.cookies.txt", remote_name);
  if (!ot_delete_cookie_at (ostree_repo_get_dfd (repo), cookie_file, domain, path, cookie_name, error))
    return FALSE;

  return TRUE;
}
gboolean
ot_remote_builtin_show_url (int argc, char **argv, GCancellable *cancellable, GError **error)
{
  g_autoptr(GOptionContext) context = NULL;
  g_autoptr(OstreeRepo) repo = NULL;
  const char *remote_name;
  g_autofree char *remote_url = NULL;
  gboolean ret = FALSE;

  context = g_option_context_new ("NAME - Show remote repository URL");

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

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

  remote_name = argv[1];

  if (ostree_repo_remote_get_url (repo, remote_name, &remote_url, error))
    {
      g_print ("%s\n", remote_url);
      ret = TRUE;
    }

 out:
  return ret;
}
Beispiel #5
0
gboolean
ostree_builtin_summary (int argc, char **argv, GCancellable *cancellable, GError **error)
{
  gboolean ret = FALSE;
  GOptionContext *context;
  gs_unref_object OstreeRepo *repo = NULL;

  context = g_option_context_new ("Manage summary metadata");

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

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

      if (!ostree_repo_regenerate_summary (repo, NULL, cancellable, error))
        goto out;
    }
  else
    {
      g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
                   "No option specified; use -u to update summary");
      goto out;
    }

  ret = TRUE;
 out:
  if (context)
    g_option_context_free (context);
  return ret;
}
static gboolean
ot_static_delta_builtin_list (int argc, char **argv, GCancellable *cancellable, GError **error)
{
  gboolean ret = FALSE;
  g_autoptr(GPtrArray) delta_names = NULL;
  guint i;
  GOptionContext *context;
  glnx_unref_object OstreeRepo *repo = NULL;

  context = g_option_context_new ("LIST - list static delta files");

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

  if (!ostree_repo_list_static_delta_names (repo, &delta_names, cancellable, error))
    goto out;
      
  if (delta_names->len == 0)
    {
      g_print ("(No static deltas)\n");
    }
  else
    {
      for (i = 0; i < delta_names->len; i++)
        {
          g_print ("%s\n", (char*)delta_names->pdata[i]);
        }
    }

  ret = TRUE;
 out:
  if (context)
    g_option_context_free (context);
  return ret;
}
Beispiel #7
0
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;
}
Beispiel #8
0
gboolean
ostree_builtin_refs (int argc, char **argv, GCancellable *cancellable, GError **error)
{
  gboolean ret = FALSE;
  GOptionContext *context;
  gs_unref_object OstreeRepo *repo = NULL;
  const char *refspec_prefix = NULL;
  gs_unref_hashtable GHashTable *refs = NULL;
  GHashTableIter hashiter;
  gpointer hashkey, hashvalue;

  context = g_option_context_new ("[PREFIX] - List refs");

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

  if (argc >= 2)
    refspec_prefix = argv[1];

  if (!ostree_repo_list_refs (repo, refspec_prefix, &refs,
                              cancellable, error))
    goto out;

  if (!opt_delete)
    {
      g_hash_table_iter_init (&hashiter, refs);
      while (g_hash_table_iter_next (&hashiter, &hashkey, &hashvalue))
        {
          const char *ref = hashkey;
          g_print ("%s\n", ref);
        }
    }
  else
    {
      g_hash_table_iter_init (&hashiter, refs);
      while (g_hash_table_iter_next (&hashiter, &hashkey, &hashvalue))
        {
          const char *refspec = hashkey;
          gs_free char *remote = NULL;
          gs_free char *ref = NULL;

          if (!ostree_parse_refspec (refspec, &remote, &ref, error))
            goto out;
          
          if (!ostree_repo_set_ref_immediate (repo, remote, ref, NULL,
                                              cancellable, error))
            goto out;
        }
    }

  ret = TRUE;
 out:
  if (context)
    g_option_context_free (context);
  if (repo)
    ostree_repo_abort_transaction (repo, cancellable, NULL);
  return ret;
}
Beispiel #9
0
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;
}
gboolean
ot_remote_builtin_gpg_import (int argc, char **argv, GCancellable *cancellable, GError **error)
{
    GOptionContext *context;
    glnx_unref_object OstreeRepo *repo = NULL;
    g_autoptr(GInputStream) source_stream = NULL;
    const char *remote_name;
    const char * const *key_ids;
    guint imported = 0;
    gboolean ret = FALSE;

    context = g_option_context_new ("NAME [KEY-ID...] - Import GPG keys");

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

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

    if (opt_stdin && opt_keyrings != NULL)
    {
        ot_util_usage_error (context, "--keyring and --stdin are mutually exclusive", error);
        goto out;
    }

    remote_name = argv[1];
    key_ids = (argc > 2) ? (const char * const *) argv + 2 : NULL;

    if (!open_source_stream (&source_stream, cancellable, error))
        goto out;

    if (!ostree_repo_remote_gpg_import (repo, remote_name, source_stream,
                                        key_ids, &imported, cancellable, error))
        goto out;

    /* XXX If we ever add internationalization, use ngettext() here. */
    g_print ("Imported %u GPG key%s to remote \"%s\"\n",
             imported, (imported == 1) ? "" : "s", remote_name);

    ret = TRUE;

out:
    g_option_context_free (context);

    return ret;
}
Beispiel #11
0
gboolean
ot_remote_builtin_list (int argc, char **argv, GCancellable *cancellable, GError **error)
{
  GOptionContext *context;
  glnx_unref_object OstreeRepo *repo = NULL;
  g_auto(GStrv) remotes = NULL;
  guint ii, n_remotes = 0;
  gboolean ret = FALSE;

  context = g_option_context_new ("- List remote repository names");

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

  remotes = ostree_repo_remote_list (repo, &n_remotes);

  if (opt_show_urls)
    {
      int max_length = 0;

      for (ii = 0; ii < n_remotes; ii++)
        max_length = MAX (max_length, strlen (remotes[ii]));

      for (ii = 0; ii < n_remotes; ii++)
        {
          g_autofree char *remote_url = NULL;

          if (!ostree_repo_remote_get_url (repo, remotes[ii], &remote_url, error))
            goto out;

          g_print ("%-*s  %s\n", max_length, remotes[ii], remote_url);
        }
    }
  else
    {
      for (ii = 0; ii < n_remotes; ii++)
        g_print ("%s\n", remotes[ii]);
    }

  ret = TRUE;

 out:
  g_option_context_free (context);

  return ret;
}
Beispiel #12
0
gboolean
ostree_builtin_cat (int argc, char **argv, GCancellable *cancellable, GError **error)
{
  GOptionContext *context;
  gs_unref_object OstreeRepo *repo = NULL;
  gboolean ret = FALSE;
  int i;
  const char *rev;
  gs_unref_object GOutputStream *stdout_stream = NULL;
  gs_unref_object GFile *root = NULL;
  gs_unref_object GFile *f = NULL;

  context = g_option_context_new ("COMMIT PATH... - Concatenate contents of files");

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

  if (argc <= 2)
    {
      ot_util_usage_error (context, "A COMMIT and at least one PATH argument are required", error);
      goto out;
    }
  rev = argv[1];

  if (!ostree_repo_read_commit (repo, rev, &root, NULL, NULL, error))
    goto out;

  stdout_stream = g_unix_output_stream_new (1, FALSE);

  for (i = 2; i < argc; i++)
    {
      g_clear_object (&f);
      f = g_file_resolve_relative_path (root, argv[i]);

      if (!cat_one_file (f, stdout_stream, cancellable, error))
        goto out;
    }
 
  ret = TRUE;
 out:
  if (context)
    g_option_context_free (context);
  return ret;
}
static gboolean
ot_static_delta_builtin_apply_offline (int argc, char **argv, GCancellable *cancellable, GError **error)
{
  gboolean ret = FALSE;
  const char *patharg;
  g_autoptr(GFile) path = NULL;
  GOptionContext *context;
  glnx_unref_object OstreeRepo *repo = NULL;

  context = g_option_context_new ("DELTA - Apply static delta file");
  if (!ostree_option_context_parse (context, apply_offline_options, &argc, &argv, OSTREE_BUILTIN_FLAG_NONE, &repo, cancellable, error))
    goto out;

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

  if (argc < 3)
    {
      g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED,
                           "PATH must be specified");
      goto out;
    }

  patharg = argv[2];
  path = g_file_new_for_path (patharg);

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

  if (!ostree_repo_static_delta_execute_offline (repo, path, TRUE, cancellable, error))
    goto out;

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

  ret = TRUE;
 out:
  if (context)
    g_option_context_free (context);
  return ret;
}
gboolean
ot_remote_builtin_list_cookies (int argc, char **argv, OstreeCommandInvocation *invocation, GCancellable *cancellable, GError **error)
{
  g_autoptr(OstreeRepo) repo = NULL;
  g_autoptr(GOptionContext) context = g_option_context_new ("NAME");

  if (!ostree_option_context_parse (context, option_entries, &argc, &argv,
                                    invocation, &repo, cancellable, error))
    return FALSE;

  if (argc < 2)
    {
      ot_util_usage_error (context, "NAME must be specified", error);
      return FALSE;
    }

  const char *remote_name = argv[1];
  g_autofree char *cookie_file = g_strdup_printf ("%s.cookies.txt", remote_name);
  if (!ot_list_cookies_at (ostree_repo_get_dfd (repo), cookie_file, error))
    return FALSE;

  return TRUE;
}
Beispiel #15
0
gboolean
ostree_builtin_init (int argc, char **argv, GCancellable *cancellable, GError **error)
{
  g_autoptr(GOptionContext) context = NULL;
  g_autoptr(OstreeRepo) repo = NULL;
  gboolean ret = FALSE;
  OstreeRepoMode mode;

  context = g_option_context_new ("- Initialize a new empty repository");

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

  if (!ostree_repo_mode_from_string (opt_mode, &mode, error))
    goto out;

  if (!ostree_repo_create (repo, mode, NULL, error))
    goto out;

  ret = TRUE;
 out:
  return ret;
}
Beispiel #16
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;
}
gboolean
ostree_builtin_trivial_httpd (int argc, char **argv, GCancellable *cancellable, GError **error)
{
  gboolean ret = FALSE;
  GOptionContext *context;
  const char *dirpath;
  OtTrivialHttpd appstruct = { 0, };
  OtTrivialHttpd *app = &appstruct;
  glnx_unref_object SoupServer *server = NULL;
  g_autoptr(GFileMonitor) dirmon = NULL;

  context = g_option_context_new ("[DIR] - Simple webserver");

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

  if (argc > 1)
    dirpath = argv[1];
  else
    dirpath = ".";

  app->root = g_file_new_for_path (dirpath);

  if (!(opt_random_500s_percentage >= 0 && opt_random_500s_percentage <= 99))
    {
      g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
                   "Invalid --random-500s=%u", opt_random_500s_percentage);
      goto out;
    }

  if (opt_log)
    {
      GOutputStream *stream = NULL;

      if (g_strcmp0 (opt_log, "-") == 0)
        {
          if (opt_daemonize)
            {
              ot_util_usage_error (context, "Cannot use --log-file=- and --daemonize at the same time", error);
              goto out;
            }
          stream = G_OUTPUT_STREAM (g_unix_output_stream_new (STDOUT_FILENO, FALSE));
        }
      else
        {
          g_autoptr(GFile) log_file;
          GFileOutputStream* log_stream;

          log_file = g_file_new_for_path (opt_log);
          log_stream = g_file_create (log_file,
                                      G_FILE_CREATE_PRIVATE,
                                      cancellable,
                                      error);
          if (!log_stream)
            goto out;
          stream = G_OUTPUT_STREAM (log_stream);
        }

      app->log = stream;
    }

#if SOUP_CHECK_VERSION(2, 48, 0)
  server = soup_server_new (SOUP_SERVER_SERVER_HEADER, "ostree-httpd ", NULL);
  if (!soup_server_listen_all (server, opt_port, 0, error))
    goto out;
#else
  server = soup_server_new (SOUP_SERVER_PORT, opt_port,
                            SOUP_SERVER_SERVER_HEADER, "ostree-httpd ",
                            NULL);
#endif

  soup_server_add_handler (server, NULL, httpd_callback, app, NULL);
  if (opt_port_file)
    {
      g_autofree char *portstr = NULL;
#if SOUP_CHECK_VERSION(2, 48, 0)
      GSList *listeners = soup_server_get_listeners (server);
      g_autoptr(GSocket) listener = NULL;
      g_autoptr(GSocketAddress) addr = NULL;
      
      g_assert (listeners);
      listener = g_object_ref (listeners->data);
      g_slist_free (listeners);
      listeners = NULL;
      addr = g_socket_get_local_address (listener, error);
      if (!addr)
        goto out;

      g_assert (G_IS_INET_SOCKET_ADDRESS (addr));
      
      portstr = g_strdup_printf ("%u\n", g_inet_socket_address_get_port ((GInetSocketAddress*)addr));
#else
      portstr = g_strdup_printf ("%u\n", soup_server_get_port (server));
#endif

      if (g_strcmp0 ("-", opt_port_file) == 0)
        {
          fputs (portstr, stdout); // not g_print - this must go to stdout, not a handler
          fflush (stdout);
        }
      else if (!g_file_set_contents (opt_port_file, portstr, strlen (portstr), error))
        goto out;
    }
#if !SOUP_CHECK_VERSION(2, 48, 0)
  soup_server_run_async (server);
#endif
  
  if (opt_daemonize)
    {
      pid_t pid = fork();
      if (pid == -1)
        {
          int errsv = errno;
          g_set_error_literal (error, G_IO_ERROR, g_io_error_from_errno (errsv),
                               g_strerror (errsv));
          goto out;
        }
      else if (pid > 0)
        {
          /* Parent */
          _exit (0);
        }
      /* Child, continue */
      /* Daemonising: close stdout/stderr so $() et al work on us */
      fclose (stdout);
      fclose (stdin);
    }
  else
    {
      /* Since we're used for testing purposes, let's just do this by
       * default.  This ensures we exit when our parent does.
       */
      if (prctl (PR_SET_PDEATHSIG, SIGTERM) != 0)
        {
          if (errno != ENOSYS)
            {
              glnx_set_error_from_errno (error);
              goto out;
            }
        }
    }

  app->running = TRUE;
  if (opt_autoexit)
    {
      gboolean is_symlink = FALSE;
      g_autoptr(GFileInfo) info = NULL;

      info = g_file_query_info (app->root,
                                G_FILE_ATTRIBUTE_STANDARD_IS_SYMLINK,
                                G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS,
                                cancellable, error);
      if (!info)
        goto out;

      is_symlink = g_file_info_get_is_symlink (info);

      if (is_symlink)
        dirmon = g_file_monitor_file (app->root, 0, cancellable, error);
      else
        dirmon = g_file_monitor_directory (app->root, 0, cancellable, error);

      if (!dirmon)
        goto out;
      g_signal_connect (dirmon, "changed", G_CALLBACK (on_dir_changed), app);
    }
  {
    g_autofree gchar *path = g_file_get_path (app->root);
    httpd_log (app, "serving at root %s\n", path);
  }
  while (app->running)
    g_main_context_iteration (NULL, TRUE);

  ret = TRUE;
 out:
  g_clear_object (&app->root);
  g_clear_object (&app->log);
  if (context)
    g_option_context_free (context);
  return ret;
}
Beispiel #18
0
gboolean
ot_remote_builtin_add (int argc, char **argv, GCancellable *cancellable, GError **error)
{
  GOptionContext *context;
  glnx_unref_object OstreeRepo *repo = NULL;
  const char *remote_name;
  const char *remote_url;
  char **iter;
  g_autofree char *target_name = NULL;
  g_autoptr(GFile) target_conf = NULL;
  g_autoptr(GVariantBuilder) optbuilder = NULL;
  gboolean ret = FALSE;

  context = g_option_context_new ("NAME [metalink=|mirrorlist=]URL [BRANCH...] - Add a remote repository");

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

  if (argc < 3)
    {
      ot_util_usage_error (context, "NAME and URL must be specified", error);
      goto out;
    }

  remote_name = argv[1];
  remote_url  = argv[2];

  optbuilder = g_variant_builder_new (G_VARIANT_TYPE ("a{sv}"));

  if (argc > 3)
    {
      g_autoptr(GPtrArray) branchesp = g_ptr_array_new ();
      int i;

      for (i = 3; i < argc; i++)
        g_ptr_array_add (branchesp, argv[i]);
      g_ptr_array_add (branchesp, NULL);

      g_variant_builder_add (optbuilder, "{s@v}",
                             "branches",
                             g_variant_new_variant (g_variant_new_strv ((const char*const*)branchesp->pdata, -1)));
    }

  /* We could just make users use --set instead for this since it's a string,
   * but e.g. when mirrorlist support is added, it'll be kinda awkward to type:
   *   --set=contenturl=mirrorlist=... */

  if (opt_contenturl != NULL)
    g_variant_builder_add (optbuilder, "{s@v}",
                           "contenturl", g_variant_new_variant (g_variant_new_string (opt_contenturl)));

  for (iter = opt_set; iter && *iter; iter++)
    {
      const char *keyvalue = *iter;
      g_autofree char *subkey = NULL;
      g_autofree char *subvalue = NULL;

      if (!ot_parse_keyvalue (keyvalue, &subkey, &subvalue, error))
        goto out;

      g_variant_builder_add (optbuilder, "{s@v}",
                             subkey, g_variant_new_variant (g_variant_new_string (subvalue)));
    }

  if (opt_no_gpg_verify)
    g_variant_builder_add (optbuilder, "{s@v}",
                           "gpg-verify",
                           g_variant_new_variant (g_variant_new_boolean (FALSE)));

  if (!ostree_repo_remote_change (repo, NULL,
                                  opt_if_not_exists ? OSTREE_REPO_REMOTE_CHANGE_ADD_IF_NOT_EXISTS : 
                                  OSTREE_REPO_REMOTE_CHANGE_ADD,
                                  remote_name, remote_url,
                                  g_variant_builder_end (optbuilder),
                                  cancellable, error))
    goto out;

  /* This is just a convenience option and is not as flexible as the full
   * "ostree remote gpg-import" command.  It imports all keys from a file,
   * which is likely the most common case.
   *
   * XXX Not sure this interacts well with if-not-exists since we don't
   *     know whether the remote already existed.  We import regardless. */
  if (opt_gpg_import != NULL)
    {
      g_autoptr(GFile) file = NULL;
      g_autoptr(GInputStream) input_stream = NULL;
      guint imported = 0;

      file = g_file_new_for_path (opt_gpg_import);
      input_stream = (GInputStream *) g_file_read (file, cancellable, error);

      if (input_stream == NULL)
        goto out;

      if (!ostree_repo_remote_gpg_import (repo, remote_name, input_stream,
                                          NULL, &imported, cancellable, error))
        goto out;

      /* XXX If we ever add internationalization, use ngettext() here. */
      g_print ("Imported %u GPG key%s to remote \"%s\"\n",
               imported, (imported == 1) ? "" : "s", remote_name);
    }

  ret = TRUE;
 out:
  g_option_context_free (context);

  return ret;
}
Beispiel #19
0
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;
}
Beispiel #20
0
gboolean
ostree_builtin_prune (int argc, char **argv, GCancellable *cancellable, GError **error)
{
  gboolean ret = FALSE;
  GOptionContext *context;
  glnx_unref_object OstreeRepo *repo = NULL;
  g_autofree char *formatted_freed_size = NULL;
  OstreeRepoPruneFlags pruneflags = 0;
  gint n_objects_total;
  gint n_objects_pruned;
  guint64 objsize_total;

  context = g_option_context_new ("- Search for unreachable objects");

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

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

  if (opt_delete_commit)
    {
      if (opt_no_prune)
        {
          ot_util_usage_error (context, "Cannot specify both --delete-commit and --no-prune", error);
          goto out;
        }
        if (opt_static_deltas_only)
          {
            if(!ostree_repo_prune_static_deltas (repo, opt_delete_commit, cancellable, error))
              goto out;
          }
        else if (!delete_commit (repo, opt_delete_commit, cancellable, error))
          goto out;
    }
  if (opt_keep_younger_than)
    {
      if (opt_no_prune)
        {
          ot_util_usage_error (context, "Cannot specify both --keep-younger-than and --no-prune", error);
          goto out;
        }
      if (!prune_commits_keep_younger_than_date (repo, opt_keep_younger_than, cancellable, error))
        goto out;
    }

  if (opt_refs_only)
    pruneflags |= OSTREE_REPO_PRUNE_FLAGS_REFS_ONLY;
  if (opt_no_prune)
    pruneflags |= OSTREE_REPO_PRUNE_FLAGS_NO_PRUNE;

  if (!ostree_repo_prune (repo, pruneflags, opt_depth,
                          &n_objects_total, &n_objects_pruned, &objsize_total,
                          cancellable, error))
    goto out;

  formatted_freed_size = g_format_size_full (objsize_total, 0);

  g_print ("Total objects: %u\n", n_objects_total);
  if (n_objects_pruned == 0)
    g_print ("No unreachable objects\n");
  else if (pruneflags & OSTREE_REPO_PRUNE_FLAGS_NO_PRUNE)
    g_print ("Would delete: %u objects, freeing %s\n",
             n_objects_pruned, formatted_freed_size);
  else
    g_print ("Deleted %u objects, %s freed\n",
             n_objects_pruned, formatted_freed_size);

  ret = TRUE;
 out:
  if (context)
    g_option_context_free (context);
  return ret;
}
Beispiel #21
0
gboolean
ostree_builtin_config (int argc, char **argv, GCancellable *cancellable, GError **error)
{
  g_autoptr(GOptionContext) context = NULL;
  glnx_unref_object OstreeRepo *repo = NULL;
  gboolean ret = FALSE;
  const char *op;
  const char *section_key;
  const char *value;
  g_autofree char *section = NULL;
  g_autofree char *key = NULL;
  GKeyFile *config = NULL;

  context = g_option_context_new ("- Change configuration settings");

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

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

  op = argv[1];

  if (!strcmp (op, "set"))
    {
      if (argc < 4)
        {
          g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
                       "KEY and VALUE must be specified");
          goto out;
        }

      section_key = argv[2];
      value = argv[3];

      if (!split_key_string (section_key, &section, &key, error))
        goto out;

      config = ostree_repo_copy_config (repo);
      g_key_file_set_string (config, section, key, value);

      if (!ostree_repo_write_config (repo, config, error))
        goto out;
    }
  else if (!strcmp (op, "get"))
    {
      GKeyFile *readonly_config = NULL;
      g_autofree char *value = NULL;
      if (argc < 3)
        {
          g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
                       "KEY must be specified");
          goto out;
        }

      section_key = argv[2];

      if (!split_key_string (section_key, &section, &key, error))
        goto out;

      readonly_config = ostree_repo_get_config (repo);
      value = g_key_file_get_string (readonly_config, section, key, error);
      if (value == NULL)
        goto out;

      g_print ("%s\n", value);
    }
  else
    {
      g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
                   "Unknown operation %s", op);
      goto out;
    }
  
  ret = TRUE;
 out:
  if (config)
    g_key_file_free (config);
  return ret;
}
Beispiel #22
0
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;
}
Beispiel #23
0
int
ostree_run (int    argc,
            char **argv,
            OstreeCommand *commands,
            GError **res_error)
{
  OstreeCommand *command;
  GError *error = NULL;
  GCancellable *cancellable = NULL;
  const char *command_name = NULL;
  gboolean success = FALSE;
  int in, out;

  /* avoid gvfs (http://bugzilla.gnome.org/show_bug.cgi?id=526454) */
  g_setenv ("GIO_USE_VFS", "local", TRUE);

  g_log_set_handler (G_LOG_DOMAIN, G_LOG_LEVEL_MESSAGE, message_handler, NULL);

  /*
   * Parse the global options. We rearrange the options as
   * necessary, in order to pass relevant options through
   * to the commands, but also have them take effect globally.
   */

  for (in = 1, out = 1; in < argc; in++, out++)
    {
      /* The non-option is the command, take it out of the arguments */
      if (argv[in][0] != '-')
        {
          if (command_name == NULL)
            {
              command_name = argv[in];
              out--;
              continue;
            }
        }

      else if (g_str_equal (argv[in], "--"))
        {
          break;
        }

      argv[out] = argv[in];
    }

  argc = out;

  command = commands;
  while (command->name)
    {
      if (g_strcmp0 (command_name, command->name) == 0)
        break;
      command++;
    }

  if (!command->fn)
    {
      g_autoptr(GOptionContext) context = NULL;
      g_autofree char *help;

      context = ostree_option_context_new_with_commands (commands);

      /* This will not return for some options (e.g. --version). */
      if (ostree_option_context_parse (context, NULL, &argc, &argv, OSTREE_BUILTIN_FLAG_NO_REPO, NULL, cancellable, &error))
        {
          if (command_name == NULL)
            {
              g_set_error_literal (&error, G_IO_ERROR, G_IO_ERROR_FAILED,
                                   "No command specified");
            }
          else
            {
              g_set_error (&error, G_IO_ERROR, G_IO_ERROR_FAILED,
                           "Unknown command '%s'", command_name);
              ostree_usage (commands, TRUE);
            }
        }

      help = g_option_context_get_help (context, FALSE, NULL);
      g_printerr ("%s", help);

      goto out;
    }

  if (!command->fn (argc, argv, cancellable, &error))
    goto out;

  success = TRUE;
 out:
  g_assert (success || error);

  if (error)
    {
      g_propagate_error (res_error, error);
      return 1;
    }
  return 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;
}
Beispiel #25
0
gboolean
ostree_builtin_export (int argc, char **argv, GCancellable *cancellable, GError **error)
{
  GOptionContext *context;
  glnx_unref_object OstreeRepo *repo = NULL;
  gboolean ret = FALSE;
  const char *rev;
  g_autoptr(GFile) root = NULL;
  g_autoptr(GFile) subtree = NULL;
  g_autofree char *commit = NULL;
  g_autoptr(GVariant) commit_data = NULL;
  struct archive *a;
  OstreeRepoExportArchiveOptions opts = { 0, };

  context = g_option_context_new ("COMMIT - Stream COMMIT to stdout in tar format");

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

#ifdef HAVE_LIBARCHIVE  

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

  a = archive_write_new ();
  /* Yes, this is hardcoded for now.  There is
   * archive_write_set_format_filter_by_ext() but it's fairly magic.
   * Many programs have support now for GNU tar, so should be a good
   * default.  I also don't want to lock us into everything libarchive
   * supports.
   */
  if (archive_write_set_format_gnutar (a) != ARCHIVE_OK)
    {
      propagate_libarchive_error (error, a);
      goto out;
    }
  if (archive_write_add_filter_none (a) != ARCHIVE_OK)
    {
      propagate_libarchive_error (error, a);
      goto out;
    }
  if (opt_output_path)
    {
      if (archive_write_open_filename (a, opt_output_path) != ARCHIVE_OK)
        {
          propagate_libarchive_error (error, a);
          goto out;
        }
    }
  else
    {
      if (archive_write_open_FILE (a, stdout) != ARCHIVE_OK)
        {
          propagate_libarchive_error (error, a);
          goto out;
        }
    }

  if (opt_no_xattrs)
    opts.disable_xattrs = TRUE;

  if (!ostree_repo_read_commit (repo, rev, &root, &commit, cancellable, error))
    goto out;

  if (!ostree_repo_load_variant (repo, OSTREE_OBJECT_TYPE_COMMIT, commit, &commit_data, error))
    goto out;

  opts.timestamp_secs = ostree_commit_get_timestamp (commit_data);

  if (opt_subpath)
    subtree = g_file_resolve_relative_path (root, opt_subpath);
  else
    subtree = g_object_ref (root);

  opts.path_prefix = opt_prefix;

  if (!ostree_repo_export_tree_to_archive (repo, &opts, (OstreeRepoFile*)subtree, a,
                                           cancellable, error))
    goto out;

  if (archive_write_close (a) != ARCHIVE_OK)
    {
      propagate_libarchive_error (error, a);
      goto out;
    }

#else
  g_set_error (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED,
               "This version of ostree is not compiled with libarchive support");
  goto out;
#endif  
  
  ret = TRUE;
 out:
  if (context)
    g_option_context_free (context);
  return ret;
}
gboolean
ot_remote_builtin_summary (int argc, char **argv, GCancellable *cancellable, GError **error)
{
  g_autoptr(GOptionContext) context = NULL;
  g_autoptr(OstreeRepo) repo = NULL;
  const char *remote_name;
  g_autoptr(GBytes) summary_bytes = NULL;
  g_autoptr(GBytes) signature_bytes = NULL;
  OstreeDumpFlags flags = OSTREE_DUMP_NONE;
  gboolean gpg_verify_summary;
  gboolean ret = FALSE;

  context = g_option_context_new ("NAME - Show remote summary");

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

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

  remote_name = argv[1];

  if (opt_cache_dir)
    {
      if (!ostree_repo_set_cache_dir (repo, AT_FDCWD, opt_cache_dir, cancellable, error))
        goto out;
    }

  if (opt_raw)
    flags |= OSTREE_DUMP_RAW;

  if (!ostree_repo_remote_fetch_summary (repo, remote_name,
                                         &summary_bytes,
                                         &signature_bytes,
                                         cancellable, error))
    goto out;

  if (summary_bytes == NULL)
    {
      g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
                   "Remote server has no summary file");
      goto out;
    }

  ot_dump_summary_bytes (summary_bytes, flags);

  if (!ostree_repo_remote_get_gpg_verify_summary (repo, remote_name,
                                                  &gpg_verify_summary,
                                                  error))
    goto out;

  if (!gpg_verify_summary)
    g_clear_pointer (&signature_bytes, g_bytes_unref);

  /* XXX Note we don't show signatures for "--raw".  My intuition is
   *     if someone needs to see or parse raw summary data, including
   *     signatures in the output would probably just interfere.
   *     If there's demand for it I suppose we could introduce a new
   *     option for raw signature data like "--raw-signatures". */
  if (signature_bytes != NULL && !opt_raw)
    {
      glnx_unref_object OstreeGpgVerifyResult *result = NULL;

      /* The actual signed summary verification happens above in
       * ostree_repo_remote_fetch_summary().  Here we just parse
       * the signatures again for the purpose of printing. */
      result = ostree_repo_verify_summary (repo,
                                           remote_name,
                                           summary_bytes,
                                           signature_bytes,
                                           cancellable,
                                           error);
      if (result == NULL)
        goto out;

      g_print ("\n");
      ostree_print_gpg_verify_result (result);
    }

  ret = TRUE;
out:
  return ret;
}
Beispiel #27
0
gboolean
ostree_admin_option_context_parse (GOptionContext *context,
                                   const GOptionEntry *main_entries,
                                   int *argc,
                                   char ***argv,
                                   OstreeAdminBuiltinFlags flags,
                                   OstreeSysroot **out_sysroot,
                                   GCancellable *cancellable,
                                   GError **error)
{
  g_autoptr(GFile) sysroot_path = NULL;
  glnx_unref_object OstreeSysroot *sysroot = NULL;
  gboolean success = FALSE;

  /* Entries are listed in --help output in the order added.  We add the
   * main entries ourselves so that we can add the --sysroot entry first. */

  g_option_context_add_main_entries (context, global_admin_entries, NULL);

  if (!ostree_option_context_parse (context, main_entries, argc, argv, OSTREE_BUILTIN_FLAG_NO_REPO, NULL, cancellable, error))
    goto out;

  if (opt_sysroot != NULL)
    sysroot_path = g_file_new_for_path (opt_sysroot);

  sysroot = ostree_sysroot_new (sysroot_path);

  if (flags & OSTREE_ADMIN_BUILTIN_FLAG_SUPERUSER)
    {
      GFile *path = ostree_sysroot_get_path (sysroot);

      /* If sysroot path is "/" then user must be root. */
      if (!g_file_has_parent (path, NULL) && getuid () != 0)
        {
          g_set_error (error, G_IO_ERROR, G_IO_ERROR_PERMISSION_DENIED,
                       "You must be root to perform this command");
          goto out;
        }
    }

  if (opt_print_current_dir)
    {
      g_autoptr(GPtrArray) deployments = NULL;
      OstreeDeployment *first_deployment;
      g_autoptr(GFile) deployment_file = NULL;
      g_autofree char *deployment_path = NULL;

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

      deployments = ostree_sysroot_get_deployments (sysroot);
      if (deployments->len == 0)
        {
          g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
                       "Unable to find a deployment in sysroot");
          goto out;
        }
      first_deployment = deployments->pdata[0];
      deployment_file = ostree_sysroot_get_deployment_directory (sysroot, first_deployment);
      deployment_path = g_file_get_path (deployment_file);

      g_print ("%s\n", deployment_path);

      /* The g_autoptr, g_autofree etc. don't happen when we explicitly
       * exit, making valgrind complain about leaks */
      g_clear_object (&sysroot);
      g_clear_object (&sysroot_path);
      g_clear_object (&deployment_file);
      g_clear_pointer (&deployments, g_ptr_array_unref);
      g_clear_pointer (&deployment_path, g_free);
      exit (EXIT_SUCCESS);
    }

  if ((flags & OSTREE_ADMIN_BUILTIN_FLAG_UNLOCKED) == 0)
    {
      /* Released when sysroot is finalized, or on process exit */
      if (!ot_admin_sysroot_lock (sysroot, error))
        goto out;
    }

  if (out_sysroot)
    *out_sysroot = g_steal_pointer (&sysroot);

  success = TRUE;

out:
  return success;
}
Beispiel #28
0
gboolean
ostree_builtin_pull (int argc, char **argv, GCancellable *cancellable, GError **error)
{
  GOptionContext *context;
  gs_unref_object OstreeRepo *repo = NULL;
  gboolean ret = FALSE;
  gs_free char *remote = NULL;
  OstreeRepoPullFlags pullflags = 0;
  GSConsole *console = NULL;
  gs_unref_ptrarray GPtrArray *refs_to_fetch = NULL;
  gs_unref_object OstreeAsyncProgress *progress = NULL;

  context = g_option_context_new ("REMOTE [BRANCH...] - Download data from remote repository");

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

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

  if (opt_disable_fsync)
    ostree_repo_set_disable_fsync (repo, TRUE);

  if (opt_mirror)
    pullflags |= OSTREE_REPO_PULL_FLAGS_MIRROR;

  if (strchr (argv[1], ':') == NULL)
    {
      remote = g_strdup (argv[1]);
      if (argc > 2)
        {
          int i;
          refs_to_fetch = g_ptr_array_new ();
          for (i = 2; i < argc; i++)
            g_ptr_array_add (refs_to_fetch, argv[i]);
          g_ptr_array_add (refs_to_fetch, NULL);
        }
    }
  else
    {
      char *ref_to_fetch;
      refs_to_fetch = g_ptr_array_new ();
      if (!ostree_parse_refspec (argv[1], &remote, &ref_to_fetch, error))
        goto out;
      /* Transfer ownership */
      g_ptr_array_add (refs_to_fetch, ref_to_fetch);
      g_ptr_array_add (refs_to_fetch, NULL);
    }

  console = gs_console_get ();
  if (console)
    {
      gs_console_begin_status_line (console, "", NULL, NULL);
      progress = ostree_async_progress_new_and_connect (ostree_repo_pull_default_console_progress_changed, console);
    }

  {
    GVariantBuilder builder;
    g_variant_builder_init (&builder, G_VARIANT_TYPE ("a{sv}"));

    if (opt_subpath)
      g_variant_builder_add (&builder, "{s@v}", "subdir",
                             g_variant_new_variant (g_variant_new_string (opt_subpath)));
    g_variant_builder_add (&builder, "{s@v}", "flags",
                           g_variant_new_variant (g_variant_new_int32 (pullflags)));
    if (refs_to_fetch)
      g_variant_builder_add (&builder, "{s@v}", "refs",
                             g_variant_new_variant (g_variant_new_strv ((const char *const*) refs_to_fetch->pdata, -1)));
    g_variant_builder_add (&builder, "{s@v}", "depth",
                           g_variant_new_variant (g_variant_new_int32 (opt_depth)));
    
    if (!ostree_repo_pull_with_options (repo, remote, g_variant_builder_end (&builder),
                                        progress, cancellable, error))
      goto out;
  }

  if (progress)
    ostree_async_progress_finish (progress);

  ret = TRUE;
 out:
  if (console)
    gs_console_end_status_line (console, NULL, NULL);
 
  if (context)
    g_option_context_free (context);
  return ret;
}
Beispiel #29
0
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;
}
Beispiel #30
0
gboolean
ostree_builtin_remote (int argc, char **argv, GCancellable *cancellable, GError **error)
{
  OstreeRemoteCommand *subcommand;
  const char *subcommand_name = NULL;
  g_autofree char *prgname = NULL;
  gboolean ret = FALSE;
  int in, out;

  for (in = 1, out = 1; in < argc; in++, out++)
    {
      /* The non-option is the command, take it out of the arguments */
      if (argv[in][0] != '-')
        {
          if (subcommand_name == NULL)
            {
              subcommand_name = argv[in];
              out--;
              continue;
            }
        }

      else if (g_str_equal (argv[in], "--"))
        {
          break;
        }

      argv[out] = argv[in];
    }

  argc = out;

  subcommand = remote_subcommands;
  while (subcommand->name)
    {
      if (g_strcmp0 (subcommand_name, subcommand->name) == 0)
        break;
      subcommand++;
    }

  if (!subcommand->name)
    {
      g_autoptr(GOptionContext) context = NULL;
      g_autofree char *help;

      context = remote_option_context_new_with_commands ();

      /* This will not return for some options (e.g. --version). */
      if (ostree_option_context_parse (context, NULL, &argc, &argv,
                                       OSTREE_BUILTIN_FLAG_NONE, NULL, cancellable, error))
        {
          if (subcommand_name == NULL)
            {
              g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED,
                                   "No \"remote\" subcommand specified");
            }
          else
            {
              g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
                           "Unknown \"remote\" subcommand '%s'", subcommand_name);
            }
        }

      help = g_option_context_get_help (context, FALSE, NULL);
      g_printerr ("%s", help);

      goto out;
    }

  prgname = g_strdup_printf ("%s %s", g_get_prgname (), subcommand_name);
  g_set_prgname (prgname);

  if (!subcommand->fn (argc, argv, cancellable, error))
    goto out;

  ret = TRUE;

 out:
  return ret;
}