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);
}
Beispiel #2
0
static gboolean
do_print_related (OstreeRepo  *repo,
                  const char *rev,
                  const char *resolved_rev,
                  GError **error)
{
  gboolean ret = FALSE;
  const char *name;
  gs_unref_variant GVariant *csum_v = NULL;
  gs_unref_variant GVariant *variant = NULL;
  gs_unref_variant GVariant *related = NULL;
  GVariantIter *viter = NULL;

  if (!ostree_repo_load_variant (repo, OSTREE_OBJECT_TYPE_COMMIT,
                                 resolved_rev, &variant, error))
    goto out;
      
  /* PARSE OSTREE_SERIALIZED_COMMIT_VARIANT */
  related = g_variant_get_child_value (variant, 2);
  
  viter = g_variant_iter_new (related);

  while (g_variant_iter_loop (viter, "(&s@ay)", &name, &csum_v))
    {
      gs_free char *checksum = ostree_checksum_from_bytes_v (csum_v);
      g_print ("%s %s\n", name, checksum);
    }
  csum_v = NULL;

  ret = TRUE;
 out:
  if (viter)
    g_variant_iter_free (viter);
  return ret;
}
Beispiel #3
0
/* Check that a given commit object is valid for the ref it was looked up via.
 * @collection_id will be %NULL for normal refs, and non-%NULL for collection–refs. */
static gboolean
fsck_commit_for_ref (OstreeRepo    *repo,
                     const char    *checksum,
                     const char    *collection_id,
                     const char    *ref_name,
                     gboolean      *found_corruption,
                     GCancellable  *cancellable,
                     GError       **error)
{
  if (!fsck_one_object (repo, checksum, OSTREE_OBJECT_TYPE_COMMIT,
                        NULL, NULL, found_corruption,
                        cancellable, error))
    return FALSE;

  /* Check the commit exists. */
  g_autoptr(GVariant) commit = NULL;
  if (!ostree_repo_load_variant (repo, OSTREE_OBJECT_TYPE_COMMIT,
                                 checksum, &commit, error))
    {
      if (collection_id != NULL)
        return glnx_prefix_error (error, "Loading commit for ref (%s, %s)",
                                  collection_id, ref_name);
      else
        return glnx_prefix_error (error, "Loading commit for ref %s", ref_name);
    }

  /* Check its bindings. */
  if (opt_verify_bindings)
    {
      if (!ostree_cmd__private__ ()->ostree_repo_verify_bindings (collection_id, ref_name, commit, error))
        return glnx_prefix_error (error, "Commit %s", checksum);
    }

  return TRUE;
}
Beispiel #4
0
static gboolean
scan_commit_object (OtPullData         *pull_data,
                    const char         *checksum,
                    guint               recursion_depth,
                    GCancellable       *cancellable,
                    GError            **error)
{
  gboolean ret = FALSE;
  gs_unref_variant GVariant *commit = NULL;
  gs_unref_variant GVariant *tree_contents_csum = NULL;
  gs_unref_variant GVariant *tree_meta_csum = NULL;
  GVariantIter *iter = NULL;

  if (recursion_depth > OSTREE_MAX_RECURSION)
    {
      g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
                   "Exceeded maximum recursion");
      goto out;
    }

#ifdef HAVE_GPGME
  if (pull_data->gpg_verify)
    {
      if (!ostree_repo_verify_commit (pull_data->repo,
                                      checksum,
                                      NULL,
                                      NULL,
                                      cancellable,
                                      error))
        goto out;
    }
#endif

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

  /* PARSE OSTREE_SERIALIZED_COMMIT_VARIANT */
  g_variant_get_child (commit, 6, "@ay", &tree_contents_csum);
  g_variant_get_child (commit, 7, "@ay", &tree_meta_csum);

  if (!scan_one_metadata_object (pull_data, ostree_checksum_bytes_peek (tree_contents_csum),
                                 OSTREE_OBJECT_TYPE_DIR_TREE, recursion_depth + 1,
                                 cancellable, error))
    goto out;

  if (!scan_one_metadata_object (pull_data, ostree_checksum_bytes_peek (tree_meta_csum),
                                 OSTREE_OBJECT_TYPE_DIR_META, recursion_depth + 1,
                                 cancellable, error))
    goto out;
  
  ret = TRUE;
 out:
  if (iter)
    g_variant_iter_free (iter);
  return ret;
}
Beispiel #5
0
static char *
version_of_commit (OstreeRepo *repo, const char *checksum)
{
  g_autoptr(GVariant) variant = NULL;
  
  /* Shouldn't fail, but if it does, we ignore it */
  if (!ostree_repo_load_variant (repo, OSTREE_OBJECT_TYPE_COMMIT, checksum,
                                 &variant, NULL))
    goto out;

  return ot_admin_checksum_version (variant);
 out:
  return NULL;
}
Beispiel #6
0
static gboolean
do_print_metadata_key (OstreeRepo     *repo,
                       const char     *resolved_rev,
                       gboolean        detached,
                       const char     *key,
                       GError        **error)
{
  gboolean ret = FALSE;
  gs_unref_variant GVariant *value = NULL;
  gs_unref_variant GVariant *commit = NULL;
  gs_unref_variant GVariant *metadata = NULL;

  if (!detached)
    {
      if (!ostree_repo_load_variant (repo, OSTREE_OBJECT_TYPE_COMMIT,
                                     resolved_rev, &commit, error))
        goto out;
      /* PARSE OSTREE_SERIALIZED_COMMIT_VARIANT */
      metadata = g_variant_get_child_value (commit, 0);
    }
  else
    {
      if (!ostree_repo_read_commit_detached_metadata (repo, resolved_rev, &metadata,
                                                      NULL, error))
        goto out;
      if (metadata == NULL)
        {
          g_set_error (error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND,
                       "No detached metadata for commit %s", resolved_rev);
          goto out;
        }
    }
  
  value = g_variant_lookup_value (metadata, key, NULL);
  if (!value)
    {
      g_set_error (error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND,
                   "No such metadata key '%s'", key);
      goto out;
    }

  ot_dump_variant (value);

  ret = TRUE;
 out:
  return ret;
}
static char *
ost_get_prev_commit (OstreeRepo *repo, char *checksum)
{
  char *ret = NULL;
  gs_unref_variant GVariant *commit = NULL;
  gs_unref_variant GVariant *parent_csum_v = NULL;
  GError *tmp_error = NULL;

  if (!ostree_repo_load_variant (repo, OSTREE_OBJECT_TYPE_COMMIT, checksum,
                                 &commit, &tmp_error))
    goto out;

  ret = ostree_commit_get_parent (commit);

 out:
  g_clear_error (&tmp_error);

  return ret;
}
Beispiel #8
0
static gboolean
print_object (OstreeRepo          *repo,
              OstreeObjectType     objtype,
              const char          *checksum,
              GError             **error)
{
  gs_unref_variant GVariant *variant = NULL;
  OstreeDumpFlags flags = OSTREE_DUMP_NONE;
  gboolean ret = FALSE;

  if (!ostree_repo_load_variant (repo, objtype, checksum,
                                 &variant, error))
    goto out;
  if (opt_raw)
    flags |= OSTREE_DUMP_RAW;
  ot_dump_object (objtype, checksum, variant, flags);

  ret = TRUE;
out:
  return ret;
}
static gboolean
metadata_version_unique (OstreeRepo  *repo,
                         const char  *checksum,
                         const char  *version,
                         GError     **error)
{
  gs_unref_variant GVariant *variant = NULL;
  gs_unref_variant GVariant *metadata = NULL;
  gs_unref_variant GVariant *value = NULL;
  gs_free gchar *parent = NULL;

  if (!ostree_repo_load_variant (repo, OSTREE_OBJECT_TYPE_COMMIT, checksum,
                                 &variant, error))
    {
      if (g_error_matches (*error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND))
        {
          g_clear_error (error);
          g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
                       "Do not have full history to validate version metadata is unique.");
        }
      goto out;
    }

  metadata = g_variant_get_child_value (variant, 0);
  if ((value = g_variant_lookup_value (metadata, "version", NULL)))
    if (g_str_equal (version, g_variant_get_string (value, NULL)))
      {
        g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
                     "Version already specified in commit %s", checksum);
        goto out;
      }
  
  if (!(parent = ostree_commit_get_parent (variant)))
    return TRUE;
  
  return metadata_version_unique (repo, parent, version, error);

 out:
  return FALSE;
}
Beispiel #10
0
static gboolean
do_print_related (OstreeRepo  *repo,
                  const char *rev,
                  const char *resolved_rev,
                  GError **error)
{
  g_autoptr(GVariant) variant = NULL;
  if (!ostree_repo_load_variant (repo, OSTREE_OBJECT_TYPE_COMMIT,
                                 resolved_rev, &variant, error))
    return FALSE;

  /* PARSE OSTREE_SERIALIZED_COMMIT_VARIANT */
  g_autoptr(GVariant) related = g_variant_get_child_value (variant, 2);
  g_autoptr(GVariantIter) viter = g_variant_iter_new (related);

  const char *name;
  GVariant* csum_v;
  while (g_variant_iter_loop (viter, "(&s@ay)", &name, &csum_v))
    {
      g_autofree char *checksum = ostree_checksum_from_bytes_v (csum_v);
      g_print ("%s %s\n", name, checksum);
    }
  return TRUE;
}
Beispiel #11
0
static gboolean
prune_commits_keep_younger_than_date (OstreeRepo *repo, const char *date, GCancellable *cancellable, GError **error)
{
  g_autoptr(GHashTable) objects = NULL;
  GHashTableIter hash_iter;
  gpointer key, value;
  struct timespec ts;
  gboolean ret = FALSE;

  if (!parse_datetime (&ts, date, NULL))
    {
      g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
                   "Could not parse '%s'", date);
      goto out;
    }

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

  if (!ostree_repo_list_objects (repo, OSTREE_REPO_LIST_OBJECTS_ALL, &objects,
                                 cancellable, error))
    goto out;

  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;
      guint64 commit_timestamp;
      g_autoptr(GVariant) commit = NULL;

      ostree_object_name_deserialize (serialized_key, &checksum, &objtype);

      if (objtype != OSTREE_OBJECT_TYPE_COMMIT)
        continue;

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

      commit_timestamp = ostree_commit_get_timestamp (commit);
      if (commit_timestamp < ts.tv_sec)
        {
          if (opt_static_deltas_only)
            {
              if(!ostree_repo_prune_static_deltas (repo, checksum, cancellable, error))
                goto out;
            }
          else
            {
              if (!ostree_repo_delete_object (repo, OSTREE_OBJECT_TYPE_COMMIT, checksum, cancellable, error))
                goto out;
            }
        }
    }

  ret = TRUE;

 out:
  return ret;
}
Beispiel #12
0
/**
 * 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;
}
Beispiel #13
0
static gboolean
scan_dirtree_object (OtPullData   *pull_data,
                     const char   *checksum,
                     int           recursion_depth,
                     GCancellable *cancellable,
                     GError      **error)
{
  gboolean ret = FALSE;
  int i, n;
  gs_unref_variant GVariant *tree = NULL;
  gs_unref_variant GVariant *files_variant = NULL;
  gs_unref_variant GVariant *dirs_variant = NULL;

  if (recursion_depth > OSTREE_MAX_RECURSION)
    {
      g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
                   "Exceeded maximum recursion");
      goto out;
    }

  if (!ostree_repo_load_variant (pull_data->repo, OSTREE_OBJECT_TYPE_DIR_TREE, checksum,
                                 &tree, error))
    goto out;

  /* PARSE OSTREE_SERIALIZED_TREE_VARIANT */
  files_variant = g_variant_get_child_value (tree, 0);
  dirs_variant = g_variant_get_child_value (tree, 1);
      
  n = g_variant_n_children (files_variant);
  for (i = 0; i < n; i++)
    {
      const char *filename;
      gboolean file_is_stored;
      gs_unref_variant GVariant *csum = NULL;
      gs_free char *file_checksum = NULL;

      g_variant_get_child (files_variant, i, "(&s@ay)", &filename, &csum);

      if (!ot_util_filename_validate (filename, error))
        goto out;

      file_checksum = ostree_checksum_from_bytes_v (csum);

      if (!ostree_repo_has_object (pull_data->repo, OSTREE_OBJECT_TYPE_FILE, file_checksum,
                                   &file_is_stored, cancellable, error))
        goto out;
      
      if (!file_is_stored && !g_hash_table_lookup (pull_data->requested_content, file_checksum))
        {
          g_hash_table_insert (pull_data->requested_content, file_checksum, file_checksum);
      
          ot_waitable_queue_push (pull_data->metadata_objects_to_fetch,
                                  pull_worker_message_new (PULL_MSG_FETCH,
                                                           ostree_object_name_serialize (file_checksum, OSTREE_OBJECT_TYPE_FILE)));
          file_checksum = NULL; /* Transfer ownership to hash */
        }
    }
      
  n = g_variant_n_children (dirs_variant);
  for (i = 0; i < n; i++)
    {
      const char *dirname;
      gs_unref_variant GVariant *tree_csum = NULL;
      gs_unref_variant GVariant *meta_csum = NULL;

      g_variant_get_child (dirs_variant, i, "(&s@ay@ay)",
                           &dirname, &tree_csum, &meta_csum);

      if (!ot_util_filename_validate (dirname, error))
        goto out;

      if (!scan_one_metadata_object (pull_data, ostree_checksum_bytes_peek (tree_csum),
                                     OSTREE_OBJECT_TYPE_DIR_TREE, recursion_depth + 1,
                                     cancellable, error))
        goto out;
      
      if (!scan_one_metadata_object (pull_data, ostree_checksum_bytes_peek (meta_csum),
                                     OSTREE_OBJECT_TYPE_DIR_META, recursion_depth + 1,
                                     cancellable, error))
        goto out;
    }

  ret = TRUE;
 out:
  return ret;
}
Beispiel #14
0
static gboolean
fsck_reachable_objects_from_commits (OtFsckData            *data,
                                     GHashTable            *commits,
                                     GCancellable          *cancellable,
                                     GError               **error)
{
  gboolean ret = FALSE;
  GHashTableIter hash_iter;
  gpointer key, value;
  ot_lhash GHashTable *reachable_objects = NULL;
  ot_lobj GInputStream *input = NULL;
  ot_lobj GFileInfo *file_info = NULL;
  ot_lvariant GVariant *xattrs = NULL;
  ot_lvariant GVariant *metadata = NULL;
  ot_lfree guchar *computed_csum = NULL;
  ot_lfree char *tmp_checksum = NULL;

  reachable_objects = ostree_traverse_new_reachable ();

  g_hash_table_iter_init (&hash_iter, commits);
  while (g_hash_table_iter_next (&hash_iter, &key, &value))
    {
      GVariant *serialized_key = key;
      const char *checksum;
      OstreeObjectType objtype;

      ostree_object_name_deserialize (serialized_key, &checksum, &objtype);

      g_assert (objtype == OSTREE_OBJECT_TYPE_COMMIT);

      if (!ostree_traverse_commit (data->repo, checksum, 0, reachable_objects,
                                   cancellable, error))
        goto out;
    }

  g_hash_table_iter_init (&hash_iter, reachable_objects);
  while (g_hash_table_iter_next (&hash_iter, &key, &value))
    {
      GVariant *serialized_key = key;
      const char *checksum;
      OstreeObjectType objtype;

      ostree_object_name_deserialize (serialized_key, &checksum, &objtype);

      g_clear_object (&input);
      g_clear_object (&file_info);
      g_clear_pointer (&xattrs, (GDestroyNotify) g_variant_unref);

      if (objtype == OSTREE_OBJECT_TYPE_COMMIT
          || objtype == OSTREE_OBJECT_TYPE_DIR_TREE 
          || objtype == OSTREE_OBJECT_TYPE_DIR_META)
        {
          g_clear_pointer (&metadata, (GDestroyNotify) g_variant_unref);
          if (!ostree_repo_load_variant (data->repo, objtype,
                                         checksum, &metadata, error))
            {
              g_prefix_error (error, "Loading metadata object %s: ", checksum);
              goto out;
            }

          if (objtype == OSTREE_OBJECT_TYPE_COMMIT)
            {
              if (!ostree_validate_structureof_commit (metadata, error))
                {
                  g_prefix_error (error, "While validating commit metadata '%s': ", checksum);
                  goto out;
                }
            }
          else if (objtype == OSTREE_OBJECT_TYPE_DIR_TREE)
            {
              if (!ostree_validate_structureof_dirtree (metadata, error))
                {
                  g_prefix_error (error, "While validating directory tree '%s': ", checksum);
                  goto out;
                }
            }
          else if (objtype == OSTREE_OBJECT_TYPE_DIR_META)
            {
              if (!ostree_validate_structureof_dirmeta (metadata, error))
                {
                  g_prefix_error (error, "While validating directory metadata '%s': ", checksum);
                  goto out;
                }
            }
          else
            g_assert_not_reached ();
          
          input = g_memory_input_stream_new_from_data (g_variant_get_data (metadata),
                                                       g_variant_get_size (metadata),
                                                       NULL);
        }
      else if (objtype == OSTREE_OBJECT_TYPE_FILE)
        {
          guint32 mode;
          if (!ostree_repo_load_file (data->repo, checksum, &input, &file_info,
                                      &xattrs, cancellable, error))
            {
              g_prefix_error (error, "Loading file object %s: ", checksum);
              goto out;
            }

          mode = g_file_info_get_attribute_uint32 (file_info, "unix::mode");
          if (!ostree_validate_structureof_file_mode (mode, error))
            {
              g_prefix_error (error, "While validating file '%s': ", checksum);
              goto out;
            }
        }
      else
        {
          g_assert_not_reached ();
        }

      g_free (computed_csum);
      if (!ostree_checksum_file_from_input (file_info, xattrs, input,
                                            objtype, &computed_csum,
                                            cancellable, error))
        goto out;

      g_free (tmp_checksum);
      tmp_checksum = ostree_checksum_from_bytes (computed_csum);
      if (strcmp (checksum, tmp_checksum) != 0)
        {
          g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
                       "corrupted object %s.%s; actual checksum: %s",
                       checksum, ostree_object_type_to_string (objtype),
                       tmp_checksum);
          goto out;
        }
    }

  ret = TRUE;
 out:
  return ret;
}
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;
}
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;
}
Beispiel #17
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;
}
Beispiel #18
0
static gboolean
prune_commits_keep_younger_than_date (OstreeRepo *repo, const char *date, GCancellable *cancellable, GError **error)
{
  g_autoptr(GHashTable) refs = NULL;
  g_autoptr(GHashTable) ref_heads = g_hash_table_new (g_str_hash, g_str_equal);
  g_autoptr(GHashTable) objects = NULL;
  GHashTableIter hash_iter;
  gpointer key, value;
  struct timespec ts;
  gboolean ret = FALSE;

  if (!parse_datetime (&ts, date, NULL))
    {
      g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
                   "Could not parse '%s'", date);
      goto out;
    }

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

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

  /* We used to prune the HEAD of a given ref by default, but that's
   * broken for a few reasons.  One is that people may use branches as
   * tags.  Second is that if we do it, we should be deleting the ref
   * too, otherwise e.g. `summary -u` breaks trying to load it, etc.
   */
  g_hash_table_iter_init (&hash_iter, refs);
  while (g_hash_table_iter_next (&hash_iter, &key, &value))
    {
      /* Value is lifecycle bound to refs */
      g_hash_table_add (ref_heads, (char*)value);
    }

  if (!ostree_repo_list_objects (repo, OSTREE_REPO_LIST_OBJECTS_ALL, &objects,
                                 cancellable, error))
    goto out;

  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;
      guint64 commit_timestamp;
      g_autoptr(GVariant) commit = NULL;

      ostree_object_name_deserialize (serialized_key, &checksum, &objtype);

      if (objtype != OSTREE_OBJECT_TYPE_COMMIT)
        continue;

      if (g_hash_table_contains (ref_heads, checksum))
        continue;

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

      commit_timestamp = ostree_commit_get_timestamp (commit);
      if (commit_timestamp < ts.tv_sec)
        {
          if (opt_static_deltas_only)
            {
              if(!ostree_repo_prune_static_deltas (repo, checksum, cancellable, error))
                goto out;
            }
          else
            {
              if (!ostree_repo_delete_object (repo, OSTREE_OBJECT_TYPE_COMMIT, checksum, cancellable, error))
                goto out;
            }
        }
    }

  ret = TRUE;

 out:
  return ret;
}
GVariant *
rpmostreed_deployment_generate_variant (OstreeDeployment *deployment,
                                        const char *booted_id,
                                        OstreeRepo *repo,
					GError **error)
{
  g_autoptr(GVariant) commit = NULL;
  g_autoptr(RpmOstreeOrigin) origin = NULL;
  g_autofree gchar *id = NULL;

  GVariant *sigs = NULL; /* floating variant */

  GVariantDict dict;

  const gchar *osname = ostree_deployment_get_osname (deployment);
  const gchar *csum = ostree_deployment_get_csum (deployment);
  gint serial = ostree_deployment_get_deployserial (deployment);
  gboolean gpg_enabled = FALSE;

  if (!ostree_repo_load_variant (repo,
				 OSTREE_OBJECT_TYPE_COMMIT,
				 csum,
				 &commit,
				 error))
    return NULL;
  
  id = rpmostreed_deployment_generate_id (deployment);

  origin = rpmostree_origin_parse_deployment (deployment, error);
  if (!origin)
    return NULL;

  g_variant_dict_init (&dict, NULL);

  g_variant_dict_insert (&dict, "id", "s", id);
  if (osname != NULL)
    g_variant_dict_insert (&dict, "osname", "s", osname);
  g_variant_dict_insert (&dict, "serial", "i", serial);
  g_variant_dict_insert (&dict, "checksum", "s", csum);
  if (rpmostree_origin_is_locally_assembled (origin))
    {
      const char *parent = ostree_commit_get_parent (commit);
      g_assert (parent);
      g_variant_dict_insert (&dict, "base-checksum", "s", parent);
      sigs = rpmostreed_deployment_gpg_results (repo, rpmostree_origin_get_refspec (origin),
                                                parent, &gpg_enabled);
    }
  else
    sigs = rpmostreed_deployment_gpg_results (repo, rpmostree_origin_get_refspec (origin),
                                              csum, &gpg_enabled);

  variant_add_commit_details (&dict, commit);
  g_variant_dict_insert (&dict, "origin", "s", rpmostree_origin_get_refspec (origin));
  if (rpmostree_origin_get_packages (origin) != NULL)
    g_variant_dict_insert (&dict, "packages", "^as", rpmostree_origin_get_packages (origin));
  if (sigs != NULL)
    g_variant_dict_insert_value (&dict, "signatures", sigs);
  g_variant_dict_insert (&dict, "gpg-enabled", "b", gpg_enabled);

  g_variant_dict_insert (&dict, "unlocked", "s",
			 ostree_deployment_unlocked_state_to_string (ostree_deployment_get_unlocked (deployment)));

  if (booted_id != NULL)
    g_variant_dict_insert (&dict, "booted", "b", g_strcmp0 (booted_id, id) == 0);

  return g_variant_dict_end (&dict);
}
static gboolean
build_content_sizenames_recurse (OstreeRepo                     *repo,
                                 OstreeRepoCommitTraverseIter   *iter,
                                 GHashTable                     *sizenames_map,
                                 GHashTable                     *include_only_objects,
                                 GCancellable                   *cancellable,
                                 GError                        **error)
{
  gboolean ret = FALSE;

  while (TRUE)
    {
      OstreeRepoCommitIterResult iterres =
        ostree_repo_commit_traverse_iter_next (iter, cancellable, error);
          
      if (iterres == OSTREE_REPO_COMMIT_ITER_RESULT_ERROR)
        goto out;
      else if (iterres == OSTREE_REPO_COMMIT_ITER_RESULT_END)
        break;
      else if (iterres == OSTREE_REPO_COMMIT_ITER_RESULT_FILE)
        {
          char *name;
          char *checksum;
          OstreeDeltaContentSizeNames *csizenames;
            
          ostree_repo_commit_traverse_iter_get_file (iter, &name, &checksum);

          if (include_only_objects && !g_hash_table_contains (include_only_objects, checksum))
            continue;

          csizenames = g_hash_table_lookup (sizenames_map, checksum);
          if (!csizenames)
            {
              g_autoptr(GFileInfo) finfo = NULL;

              if (!ostree_repo_load_file (repo, checksum,
                                          NULL, &finfo, NULL,
                                          cancellable, error))
                goto out;

              if (g_file_info_get_file_type (finfo) != G_FILE_TYPE_REGULAR)
                continue;

              csizenames = g_new0 (OstreeDeltaContentSizeNames, 1);
              csizenames->checksum = g_strdup (checksum);
              csizenames->size = g_file_info_get_size (finfo);
              g_hash_table_replace (sizenames_map, csizenames->checksum, csizenames);
            }

          if (!csizenames->basenames)
            csizenames->basenames = g_ptr_array_new_with_free_func (g_free);
          g_ptr_array_add (csizenames->basenames, g_strdup (name));
        }
      else if (iterres == OSTREE_REPO_COMMIT_ITER_RESULT_DIR)
        {
          char *name;
          char *content_checksum;
          char *meta_checksum;
          g_autoptr(GVariant) dirtree = NULL;
          ostree_cleanup_repo_commit_traverse_iter
            OstreeRepoCommitTraverseIter subiter = { 0, };

          ostree_repo_commit_traverse_iter_get_dir (iter, &name, &content_checksum, &meta_checksum);
          
          if (!ostree_repo_load_variant (repo, OSTREE_OBJECT_TYPE_DIR_TREE,
                                         content_checksum, &dirtree,
                                         error))
            goto out;

          if (!ostree_repo_commit_traverse_iter_init_dirtree (&subiter, repo, dirtree,
                                                              OSTREE_REPO_COMMIT_TRAVERSE_FLAG_NONE,
                                                              error))
            goto out;

          if (!build_content_sizenames_recurse (repo, &subiter,
                                                sizenames_map, include_only_objects,
                                                cancellable, error))
            goto out;
        }
      else
        g_assert_not_reached ();
    }
  ret = TRUE;
 out:
  return ret;
}
static gboolean
generate_all_deltas (OstreeRepo   *repo,
                     GPtrArray   **unwanted_deltas,
                     GCancellable *cancellable,
                     GError      **error)
{
  g_autoptr(GHashTable) all_refs = NULL;
  g_autoptr(GHashTable) all_deltas_hash = NULL;
  g_autoptr(GHashTable) wanted_deltas_hash = NULL;
  g_autoptr(GPtrArray) all_deltas = NULL;
  int i;
  GHashTableIter iter;
  gpointer key, value;
  g_autoptr(GVariantBuilder) parambuilder = NULL;
  g_autoptr(GVariant) params = NULL;
  int n_spawned_delta_generate = 0;
  g_autoptr(GMainContextPopDefault) context = NULL;
  g_autoptr(GPtrArray) ignore_patterns = g_ptr_array_new_with_free_func ((GDestroyNotify)g_pattern_spec_free);

  g_print ("Generating static deltas\n");

  parambuilder = g_variant_builder_new (G_VARIANT_TYPE ("a{sv}"));
  /* Fall back for 1 meg files */
  g_variant_builder_add (parambuilder, "{sv}",
                         "min-fallback-size", g_variant_new_uint32 (1));
  params = g_variant_ref_sink (g_variant_builder_end (parambuilder));

  if (!ostree_repo_list_static_delta_names (repo, &all_deltas,
                                            cancellable, error))
    return FALSE;

  wanted_deltas_hash = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);

  all_deltas_hash = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
  for (i = 0; i < all_deltas->len; i++)
    g_hash_table_insert (all_deltas_hash,
                         g_strdup (g_ptr_array_index (all_deltas, i)),
                         NULL);

  if (!ostree_repo_list_refs (repo, NULL, &all_refs,
                              cancellable, error))
    return FALSE;

  context = flatpak_main_context_new_default ();

  if (opt_static_delta_ignore_refs != NULL)
    {
      for (i = 0; opt_static_delta_ignore_refs[i] != NULL; i++)
        g_ptr_array_add (ignore_patterns,
                         g_pattern_spec_new (opt_static_delta_ignore_refs[i]));
    }

  g_hash_table_iter_init (&iter, all_refs);
  while (g_hash_table_iter_next (&iter, &key, &value))
    {
      const char *ref = key;
      const char *commit = value;
      g_autoptr(GVariant) variant = NULL;
      g_autoptr(GVariant) parent_variant = NULL;
      g_autofree char *parent_commit = NULL;
      g_autofree char *grandparent_commit = NULL;
      gboolean ignore_ref = FALSE;

      if (g_str_has_prefix (ref, "app/") || g_str_has_prefix (ref, "runtime/"))
        {
          g_auto(GStrv) parts = g_strsplit (ref, "/", 4);

          for (i = 0; i < ignore_patterns->len; i++)
            {
              GPatternSpec *pattern = g_ptr_array_index(ignore_patterns, i);
              if (g_pattern_match_string (pattern, parts[1]))
                {
                  ignore_ref = TRUE;
                  break;
                }
            }

        }
      else if (g_str_has_prefix (ref, "appstream/"))
        {
          /* Old appstream branch deltas poorly, and most users handle the new format */
          ignore_ref = TRUE;
        }
      else if (g_str_has_prefix (ref, "appstream2/"))
        {
          /* Always delta this */
          ignore_ref = FALSE;
        }
      else
        {
          /* Ignore unknown ref types */
          ignore_ref = FALSE;
        }

      if (ignore_ref)
        {
          g_debug ("Ignoring deltas for ref %s", ref);
          continue;
        }

      if (!ostree_repo_load_variant (repo, OSTREE_OBJECT_TYPE_COMMIT, commit,
                                     &variant, NULL))
        {
          g_warning ("Couldn't load commit %s", commit);
          continue;
        }

      /* From empty */
      if (!g_hash_table_contains (all_deltas_hash, commit))
        {
          if (!spawn_delta_generation (context, &n_spawned_delta_generate, repo, params,
                                       ref, NULL, commit,
                                       error))
            return FALSE;
        }

      /* Mark this one as wanted */
      g_hash_table_insert (wanted_deltas_hash, g_strdup (commit), GINT_TO_POINTER (1));

      parent_commit = ostree_commit_get_parent (variant);

      if (parent_commit != NULL &&
          !ostree_repo_load_variant (repo, OSTREE_OBJECT_TYPE_COMMIT, parent_commit,
                                     &parent_variant, NULL))
        {
          g_warning ("Couldn't load parent commit %s", parent_commit);
          continue;
        }

      /* From parent */
      if (parent_variant != NULL)
        {
          g_autofree char *from_parent = g_strdup_printf ("%s-%s", parent_commit, commit);

          if (!g_hash_table_contains (all_deltas_hash, from_parent))
            {
              if (!spawn_delta_generation (context, &n_spawned_delta_generate, repo, params,
                                           ref, parent_commit, commit,
                                           error))
                return FALSE;
            }

          /* Mark parent-to-current as wanted */
          g_hash_table_insert (wanted_deltas_hash, g_strdup (from_parent), GINT_TO_POINTER (1));

          /* We also want to keep around the parent and the grandparent-to-parent deltas
           * because otherwise these will be deleted immediately which may cause a race if
           * someone is currently downloading them.
           * However, there is no need to generate these if they don't exist.
           */

          g_hash_table_insert (wanted_deltas_hash, g_strdup (parent_commit), GINT_TO_POINTER (1));
          grandparent_commit = ostree_commit_get_parent (parent_variant);
          if (grandparent_commit != NULL)
            g_hash_table_insert (wanted_deltas_hash,
                                 g_strdup_printf ("%s-%s", grandparent_commit, parent_commit),
                                 GINT_TO_POINTER (1));
        }
    }

  while (n_spawned_delta_generate > 0)
    g_main_context_iteration (context, TRUE);

  *unwanted_deltas = g_ptr_array_new_with_free_func (g_free);
  for (i = 0; i < all_deltas->len; i++)
    {
      const char *delta = g_ptr_array_index (all_deltas, i);
      if (!g_hash_table_contains (wanted_deltas_hash, delta))
        g_ptr_array_add (*unwanted_deltas, g_strdup (delta));
    }

  return TRUE;
}
/**
 * ostree_repo_commit_traverse_iter_next:
 * @iter: An iter
 * @cancellable: Cancellable
 * @error: Error
 *
 * Step the interator to the next item.  Files will be returned first,
 * then subdirectories.  Call this in a loop; upon encountering
 * %OSTREE_REPO_COMMIT_ITER_RESULT_END, there will be no more files or
 * directories.  If %OSTREE_REPO_COMMIT_ITER_RESULT_DIR is returned,
 * then call ostree_repo_commit_traverse_iter_get_dir() to retrieve
 * data for that directory.  Similarly, if
 * %OSTREE_REPO_COMMIT_ITER_RESULT_FILE is returned, call
 * ostree_repo_commit_traverse_iter_get_file().
 * 
 * If %OSTREE_REPO_COMMIT_ITER_RESULT_ERROR is returned, it is a
 * program error to call any further API on @iter except for
 * ostree_repo_commit_traverse_iter_clear().
 */
OstreeRepoCommitIterResult
ostree_repo_commit_traverse_iter_next (OstreeRepoCommitTraverseIter *iter,
                                       GCancellable                 *cancellable,
                                       GError                      **error)
{
  struct _OstreeRepoRealCommitTraverseIter *real =
    (struct _OstreeRepoRealCommitTraverseIter*)iter;
  OstreeRepoCommitIterResult res = OSTREE_REPO_COMMIT_ITER_RESULT_ERROR;

  if (!real->current_dir)
    {
      if (!ostree_repo_load_variant (real->repo, OSTREE_OBJECT_TYPE_DIR_TREE,
                                     real->checksum_content,
                                     &real->current_dir,
                                     error))
        goto out;
      res = OSTREE_REPO_COMMIT_ITER_RESULT_DIR;
    }
  else
    {
      guint nfiles;
      guint ndirs;
      guint idx;
      const guchar *csum;
      g_autoptr(GVariant) content_csum_v = NULL;
      g_autoptr(GVariant) meta_csum_v = NULL;
      g_autoptr(GVariant) files_variant = NULL;
      g_autoptr(GVariant) dirs_variant = NULL;

      files_variant = g_variant_get_child_value (real->current_dir, 0);
      dirs_variant = g_variant_get_child_value (real->current_dir, 1);

      nfiles = g_variant_n_children (files_variant);
      ndirs = g_variant_n_children (dirs_variant);
      if (real->idx < nfiles)
        {
          idx = real->idx;
          g_variant_get_child (files_variant, idx, "(&s@ay)",
                               &real->name,
                               &content_csum_v);

          csum = ostree_checksum_bytes_peek_validate (content_csum_v, error);
          if (!csum)
            goto out;
          ostree_checksum_inplace_from_bytes (csum, real->checksum_content);

          res = OSTREE_REPO_COMMIT_ITER_RESULT_FILE;

          real->idx++;
        }
      else if (real->idx < nfiles + ndirs)
        {
          idx = real->idx - nfiles;

          g_variant_get_child (dirs_variant, idx, "(&s@ay@ay)",
                               &real->name, &content_csum_v, &meta_csum_v);

          csum = ostree_checksum_bytes_peek_validate (content_csum_v, error);
          if (!csum)
            goto out;
          ostree_checksum_inplace_from_bytes (csum, real->checksum_content);

          csum = ostree_checksum_bytes_peek_validate (meta_csum_v, error);
          if (!csum)
            goto out;
          ostree_checksum_inplace_from_bytes (csum, real->checksum_meta);
          
          res = OSTREE_REPO_COMMIT_ITER_RESULT_DIR;

          real->idx++;
        }
      else
        res = OSTREE_REPO_COMMIT_ITER_RESULT_END;
    }
  
  real->state = res;
 out:
  return res;
}
static gboolean
install_packages_in_root (RpmOstreeTreeComposeContext  *self,
                          RpmOstreeContext *ctx,
                          JsonObject      *treedata,
                          GFile           *yumroot,
                          char           **packages,
                          gboolean        *out_unmodified,
                          char           **out_new_inputhash,
                          GCancellable    *cancellable,
                          GError         **error)
{
  gboolean ret = FALSE;
  guint progress_sigid;
  GFile *contextdir = self->treefile_context_dirs->pdata[0];
  g_autoptr(RpmOstreeInstall) hifinstall = { 0, };
  DnfContext *hifctx;
  g_autofree char *ret_new_inputhash = NULL;
  g_autoptr(GKeyFile) treespec = g_key_file_new ();
  JsonArray *enable_repos = NULL;
  JsonArray *add_files = NULL;

  /* TODO - uncomment this once we have SELinux working */
#if 0
  g_autofree char *cache_repo_pathstr = glnx_fdrel_abspath (self->cachedir_dfd, "repo");
  g_autoptr(GFile) cache_repo_path = g_file_new_for_path (cache_repo_pathstr);
  glnx_unref_object OstreeRepo *ostreerepo = ostree_repo_new (cache_repo_path);

  if (!g_file_test (cache_repo_pathstr, G_FILE_TEST_EXISTS))
    {
      if (!ostree_repo_create (ostreerepo, OSTREE_REPO_MODE_BARE_USER, cancellable, error))
        goto out;
    }
#endif

  hifctx = rpmostree_context_get_hif (ctx);
  if (opt_proxy)
    dnf_context_set_http_proxy (hifctx, opt_proxy);

  /* Hack this here... see https://github.com/rpm-software-management/libhif/issues/53
   * but in the future we won't be using librpm at all for unpack/scripts, so it won't
   * matter.
   */
  { const char *debuglevel = getenv ("RPMOSTREE_RPM_VERBOSITY");
    if (!debuglevel)
      debuglevel = "info";
    dnf_context_set_rpm_verbosity (hifctx, debuglevel);
    rpmlogSetFile(NULL);
  }

  dnf_context_set_repo_dir (hifctx, gs_file_get_path_cached (contextdir));

  /* By default, retain packages in addition to metadata with --cachedir */
  if (opt_cachedir)
    dnf_context_set_keep_cache (hifctx, TRUE);
  if (opt_cache_only)
    dnf_context_set_cache_age (hifctx, G_MAXUINT);

  g_key_file_set_string (treespec, "tree", "ref", self->ref);
  g_key_file_set_string_list (treespec, "tree", "packages", (const char *const*)packages, g_strv_length (packages));

  /* Some awful code to translate between JSON and GKeyFile */
  if (json_object_has_member (treedata, "install-langs"))
    {
      JsonArray *a = json_object_get_array_member (treedata, "install-langs");
      if (!set_keyfile_string_array_from_json (treespec, "tree", "install-langs", a, error))
        goto out;
    }

  /* Bind the json \"repos\" member to the hif state, which looks at the
   * enabled= member of the repos file.  By default we forcibly enable
   * only repos which are specified, ignoring the enabled= flag.
   */
  if (!json_object_has_member (treedata, "repos"))
    {
      g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED,
                           "Treefile is missing required \"repos\" member");
      goto out;
    }

  enable_repos = json_object_get_array_member (treedata, "repos");

  if (!set_keyfile_string_array_from_json (treespec, "tree", "repos", enable_repos, error))
    goto out;

  { gboolean docs = TRUE;

    if (!_rpmostree_jsonutil_object_get_optional_boolean_member (treedata,
                                                                 "documentation",
                                                                 &docs,
                                                                 error))
      goto out;

    if (!docs)
      g_key_file_set_boolean (treespec, "tree", "documentation", FALSE);
  }

  { g_autoptr(GError) tmp_error = NULL;
    g_autoptr(RpmOstreeTreespec) treespec_value = rpmostree_treespec_new_from_keyfile (treespec, &tmp_error);
    g_assert_no_error (tmp_error);
    
    if (!rpmostree_context_setup (ctx, gs_file_get_path_cached (yumroot), "/", treespec_value,
                                  cancellable, error))
      goto out;
  }

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

  if (!rpmostree_context_prepare_install (ctx, &hifinstall, cancellable, error))
    goto out;

  rpmostree_print_transaction (hifctx);

  if (json_object_has_member (treedata, "add-files"))
    add_files = json_object_get_array_member (treedata, "add-files");

  /* FIXME - just do a depsolve here before we compute download requirements */
  if (!compute_checksum_from_treefile_and_goal (self, dnf_context_get_goal (hifctx),
                                                contextdir, add_files,
                                                &ret_new_inputhash, error))
    goto out;

  /* Only look for previous checksum if caller has passed *out_unmodified */
  if (self->previous_checksum && out_unmodified != NULL)
    {
      g_autoptr(GVariant) commit_v = NULL;
      g_autoptr(GVariant) commit_metadata = NULL;
      const char *previous_inputhash = NULL;
      
      if (!ostree_repo_load_variant (self->repo, OSTREE_OBJECT_TYPE_COMMIT,
                                     self->previous_checksum,
                                     &commit_v, error))
        goto out;

      commit_metadata = g_variant_get_child_value (commit_v, 0);
      if (g_variant_lookup (commit_metadata, "rpmostree.inputhash", "&s", &previous_inputhash))
        {
          if (strcmp (previous_inputhash, ret_new_inputhash) == 0)
            {
              *out_unmodified = TRUE;
              ret = TRUE;
              goto out;
            }
        }
      else
        g_print ("Previous commit found, but without rpmostree.inputhash metadata key\n");
    }

  if (opt_dry_run)
    {
      ret = TRUE;
      goto out;
    }

  /* --- Downloading packages --- */
  if (!rpmostree_context_download (ctx, hifinstall, cancellable, error))
    goto out;
  
  { g_auto(GLnxConsoleRef) console = { 0, };
    glnx_unref_object DnfState *hifstate = dnf_state_new ();

    progress_sigid = g_signal_connect (hifstate, "percentage-changed",
                                     G_CALLBACK (on_hifstate_percentage_changed), 
                                     "Installing packages:");

    glnx_console_lock (&console);

    if (!dnf_transaction_commit (dnf_context_get_transaction (hifctx),
                                 dnf_context_get_goal (hifctx),
                                 hifstate,
                                 error))
      goto out;

    g_signal_handler_disconnect (hifstate, progress_sigid);
  }
      
  ret = TRUE;
  if (out_unmodified)
    *out_unmodified = FALSE;
  gs_transfer_out_value (out_new_inputhash, &ret_new_inputhash);
 out:
  return ret;
}
Beispiel #24
0
static gboolean
load_and_fsck_one_object (OstreeRepo            *repo,
                          const char            *checksum,
                          OstreeObjectType       objtype,
                          gboolean              *out_found_corruption,
                          GCancellable          *cancellable,
                          GError               **error)
{
  gboolean ret = FALSE;
  gboolean missing = FALSE;
  gs_unref_variant GVariant *metadata = NULL;
  gs_unref_object GInputStream *input = NULL;
  gs_unref_object GFileInfo *file_info = NULL;
  gs_unref_variant GVariant *xattrs = NULL;
  GError *temp_error = NULL;

  if (OSTREE_OBJECT_TYPE_IS_META (objtype))
    {
      if (!ostree_repo_load_variant (repo, objtype,
                                     checksum, &metadata, &temp_error))
        {
          if (g_error_matches (temp_error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND))
            {
              g_clear_error (&temp_error);
              g_printerr ("Object missing: %s.%s\n", checksum,
                          ostree_object_type_to_string (objtype));
              missing = TRUE;
            }
          else
            {
              g_prefix_error (error, "Loading metadata object %s: ", checksum);
              goto out;
            }
        }
      else
        {
          if (objtype == OSTREE_OBJECT_TYPE_COMMIT)
            {
              if (!ostree_validate_structureof_commit (metadata, error))
                {
                  g_prefix_error (error, "While validating commit metadata '%s': ", checksum);
                  goto out;
                }
            }
          else if (objtype == OSTREE_OBJECT_TYPE_DIR_TREE)
            {
              if (!ostree_validate_structureof_dirtree (metadata, error))
                {
                  g_prefix_error (error, "While validating directory tree '%s': ", checksum);
                  goto out;
                }
            }
          else if (objtype == OSTREE_OBJECT_TYPE_DIR_META)
            {
              if (!ostree_validate_structureof_dirmeta (metadata, error))
                {
                  g_prefix_error (error, "While validating directory metadata '%s': ", checksum);
                  goto out;
                }
            }
      
          input = g_memory_input_stream_new_from_data (g_variant_get_data (metadata),
                                                       g_variant_get_size (metadata),
                                                       NULL);

        }
    }
  else
    {
      guint32 mode;
      g_assert (objtype == OSTREE_OBJECT_TYPE_FILE);
      if (!ostree_repo_load_file (repo, checksum, &input, &file_info,
                                  &xattrs, cancellable, &temp_error))
        {
          if (g_error_matches (temp_error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND))
            {
              g_clear_error (&temp_error);
              g_printerr ("Object missing: %s.%s\n", checksum,
                          ostree_object_type_to_string (objtype));
              missing = TRUE;
            }
          else
            {
              g_prefix_error (error, "Loading file object %s: ", checksum);
              goto out;
            }
        }
      else
        {
          mode = g_file_info_get_attribute_uint32 (file_info, "unix::mode");
          if (!ostree_validate_structureof_file_mode (mode, error))
            {
              g_prefix_error (error, "While validating file '%s': ", checksum);
              goto out;
            }
        }
    }

  if (missing)
    {
      *out_found_corruption = TRUE;
    }
  else
    {
      gs_free guchar *computed_csum = NULL;
      gs_free char *tmp_checksum = NULL;

      if (!ostree_checksum_file_from_input (file_info, xattrs, input,
                                            objtype, &computed_csum,
                                            cancellable, error))
        goto out;
      
      tmp_checksum = ostree_checksum_from_bytes (computed_csum);
      if (strcmp (checksum, tmp_checksum) != 0)
        {
          gs_free char *msg = g_strdup_printf ("corrupted object %s.%s; actual checksum: %s",
                                               checksum, ostree_object_type_to_string (objtype),
                                               tmp_checksum);
          if (opt_delete)
            {
              g_printerr ("%s\n", msg);
              (void) ostree_repo_delete_object (repo, objtype, checksum, cancellable, NULL);
              *out_found_corruption = TRUE;
            }
          else
            {
              g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED, msg);
              goto out;
            }
        }
    }

  ret = TRUE;
 out:
  return ret;
}
GVariant *
rpmostreed_deployment_generate_variant (OstreeDeployment *deployment,
                                        OstreeRepo *repo)
{
  g_autoptr(GVariant) commit = NULL;

  g_autofree gchar *origin_refspec = NULL;
  g_autofree gchar *version_commit = NULL;
  g_autofree gchar *id = NULL;

  GVariant *sigs = NULL; /* floating variant */
  GError *error = NULL;

  GVariantDict dict;
  guint64 timestamp = 0;

  const gchar *osname = ostree_deployment_get_osname (deployment);
  const gchar *csum = ostree_deployment_get_csum (deployment);
  gint serial = ostree_deployment_get_deployserial (deployment);
  id = rpmostreed_deployment_generate_id (deployment);

  if (ostree_repo_load_variant (repo,
                                OSTREE_OBJECT_TYPE_COMMIT,
                                csum,
                                &commit,
                                &error))
    {
      g_autoptr(GVariant) metadata = NULL;
      timestamp = ostree_commit_get_timestamp (commit);
      metadata = g_variant_get_child_value (commit, 0);
      if (metadata != NULL)
          g_variant_lookup (metadata, "version", "s", &version_commit);
    }
  else
    {
      g_warning ("Error loading commit %s", error->message);
    }
  g_clear_error (&error);

  origin_refspec = rpmostreed_deployment_get_refspec (deployment);
  if (origin_refspec)
    sigs = rpmostreed_deployment_gpg_results (repo, origin_refspec, csum);

  g_variant_dict_init (&dict, NULL);

  g_variant_dict_insert (&dict, "id", "s", id);
  if (osname != NULL)
    g_variant_dict_insert (&dict, "osname", "s", osname);
  g_variant_dict_insert (&dict, "serial", "i", serial);
  g_variant_dict_insert (&dict, "checksum", "s", csum);
  if (version_commit != NULL)
    g_variant_dict_insert (&dict, "version", "s", version_commit);
  if (timestamp > 0)
    g_variant_dict_insert (&dict, "timestamp", "t", timestamp);
  if (origin_refspec != NULL)
    g_variant_dict_insert (&dict, "origin", "s", origin_refspec);
  if (sigs != NULL)
    g_variant_dict_insert_value (&dict, "signatures", sigs);

  return g_variant_dict_end (&dict);
}
Beispiel #26
0
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;
}
gboolean
ot_admin_builtin_status (int argc, char **argv, OstreeCommandInvocation *invocation, GCancellable *cancellable, GError **error)
{
  const int is_tty = isatty (1);
  const char *red_bold_prefix = is_tty ? "\x1b[31m\x1b[1m" : "";
  const char *red_bold_suffix = is_tty ? "\x1b[22m\x1b[0m" : "";

  g_autoptr(GOptionContext) context = g_option_context_new ("");

  g_autoptr(OstreeSysroot) sysroot = NULL;
  if (!ostree_admin_option_context_parse (context, options, &argc, &argv,
                                          OSTREE_ADMIN_BUILTIN_FLAG_UNLOCKED,
                                          invocation, &sysroot, cancellable, error))
    return FALSE;

  g_autoptr(OstreeRepo) repo = NULL;
  if (!ostree_sysroot_get_repo (sysroot, &repo, cancellable, error))
    return FALSE;

  g_autoptr(GPtrArray) deployments = ostree_sysroot_get_deployments (sysroot);
  OstreeDeployment *booted_deployment = ostree_sysroot_get_booted_deployment (sysroot);

  g_autoptr(OstreeDeployment) pending_deployment = NULL;
  g_autoptr(OstreeDeployment) rollback_deployment = NULL;
  if (booted_deployment)
    ostree_sysroot_query_deployments_for (sysroot, NULL, &pending_deployment,
                                          &rollback_deployment);

  if (deployments->len == 0)
    {
      g_print ("No deployments.\n");
    }
  else
    {
      for (guint i = 0; i < deployments->len; i++)
        {
          OstreeDeployment *deployment = deployments->pdata[i];
          const char *ref = ostree_deployment_get_csum (deployment);

          /* Load the backing commit; shouldn't normally fail, but if it does,
           * we stumble on.
           */
          g_autoptr(GVariant) commit = NULL;
          (void)ostree_repo_load_variant (repo, OSTREE_OBJECT_TYPE_COMMIT, ref,
                                          &commit, NULL);
          g_autoptr(GVariant) commit_metadata = NULL;
          if (commit)
            commit_metadata = g_variant_get_child_value (commit, 0);

          const char *version = NULL;
          const char *source_title = NULL;
          if (commit_metadata)
            {
              (void) g_variant_lookup (commit_metadata, OSTREE_COMMIT_META_KEY_VERSION, "&s", &version);
              (void) g_variant_lookup (commit_metadata, OSTREE_COMMIT_META_KEY_SOURCE_TITLE, "&s", &source_title);
            }

          GKeyFile *origin = ostree_deployment_get_origin (deployment);

          const char *deployment_status = "";
          if (deployment == pending_deployment)
            deployment_status = " (pending)";
          else if (deployment == rollback_deployment)
            deployment_status = " (rollback)";
          g_print ("%c %s %s.%d%s\n",
                   deployment == booted_deployment ? '*' : ' ',
                   ostree_deployment_get_osname (deployment),
                   ostree_deployment_get_csum (deployment),
                   ostree_deployment_get_deployserial (deployment),
                   deployment_status);
          if (version)
            g_print ("    Version: %s\n", version);

          OstreeDeploymentUnlockedState unlocked = ostree_deployment_get_unlocked (deployment);
          switch (unlocked)
            {
            case OSTREE_DEPLOYMENT_UNLOCKED_NONE:
              break;
            default:
              g_print ("    %sUnlocked: %s%s\n", red_bold_prefix,
                       ostree_deployment_unlocked_state_to_string (unlocked),
                       red_bold_suffix);
            }
          if (!origin)
            g_print ("    origin: none\n");
          else
            {
              g_autofree char *origin_refspec = g_key_file_get_string (origin, "origin", "refspec", NULL);
              if (!origin_refspec)
                g_print ("    origin: <unknown origin type>\n");
              else
                g_print ("    origin refspec: %s\n", origin_refspec);
              if (source_title)
                g_print ("    `- %s\n", source_title);
            }

          if (deployment_get_gpg_verify (deployment, repo))
            {
              g_autoptr(GString) output_buffer = g_string_sized_new (256);
              /* Print any digital signatures on this commit. */

              g_autoptr(GError) local_error = NULL;
              g_autoptr(OstreeGpgVerifyResult) result =
                ostree_repo_verify_commit_ext (repo, ref, NULL, NULL,
                                               cancellable, &local_error);

              /* G_IO_ERROR_NOT_FOUND just means the commit is not signed. */
              if (g_error_matches (local_error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND))
                {
                  g_clear_error (&local_error);
                  continue;
                }
              else if (local_error != NULL)
                {
                  g_propagate_error (error, g_steal_pointer (&local_error));
                  return FALSE;
                }

              const guint n_signatures = ostree_gpg_verify_result_count_all (result);
              for (guint jj = 0; jj < n_signatures; jj++)
                {
                  ostree_gpg_verify_result_describe (result, jj, output_buffer, "    GPG: ",
                                                     OSTREE_GPG_SIGNATURE_FORMAT_DEFAULT);
                }

              g_print ("%s", output_buffer->str);
            }
        }
    }

  return TRUE;
}
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;
}
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;
}
int
rpmostree_compose_builtin_tree (int             argc,
                                char          **argv,
                                GCancellable   *cancellable,
                                GError        **error)
{
  int exit_status = EXIT_FAILURE;
  GError *temp_error = NULL;
  GOptionContext *context = g_option_context_new ("TREEFILE - Run yum and commit the result to an OSTree repository");
  RpmOstreeTreeComposeContext selfdata = { NULL, };
  RpmOstreeTreeComposeContext *self = &selfdata;
  JsonNode *treefile_rootval = NULL;
  JsonObject *treefile = NULL;
  g_autofree char *cachekey = NULL;
  g_autofree char *new_inputhash = NULL;
  g_autoptr(GFile) previous_root = NULL;
  g_autofree char *previous_checksum = NULL;
  g_autoptr(GFile) yumroot = NULL;
  g_autoptr(GFile) yumroot_varcache = NULL;
  glnx_fd_close int rootfs_fd = -1;
  glnx_unref_object OstreeRepo *repo = NULL;
  g_autoptr(GPtrArray) bootstrap_packages = NULL;
  g_autoptr(GPtrArray) packages = NULL;
  g_autoptr(GFile) treefile_path = NULL;
  g_autoptr(GFile) treefile_dirpath = NULL;
  g_autoptr(GFile) repo_path = NULL;
  glnx_unref_object JsonParser *treefile_parser = NULL;
  gs_unref_variant_builder GVariantBuilder *metadata_builder = 
    g_variant_builder_new (G_VARIANT_TYPE ("a{sv}"));
  g_autoptr(RpmOstreeContext) corectx = NULL;
  g_autoptr(GHashTable) varsubsts = NULL;
  gboolean workdir_is_tmp = FALSE;
  g_autofree char *next_version = NULL;

  self->treefile_context_dirs = g_ptr_array_new_with_free_func ((GDestroyNotify)g_object_unref);
  
  if (!rpmostree_option_context_parse (context,
                                       option_entries,
                                       &argc, &argv,
                                       RPM_OSTREE_BUILTIN_FLAG_LOCAL_CMD,
                                       cancellable,
                                       NULL,
                                       error))
    goto out;

  if (argc < 2)
    {
      rpmostree_usage_error (context, "TREEFILE must be specified", error);
      goto out;
    }
  
  if (!opt_repo)
    {
      rpmostree_usage_error (context, "--repo must be specified", error);
      goto out;
    }

  if (getuid () != 0)
    {
      g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
                   "compose tree must presently be run as uid 0 (root)");
      goto out;
    }

  /* Test whether or not bwrap is going to work - we will fail inside e.g. a Docker
   * container without --privileged or userns exposed.
   */
  if (!rpmostree_bwrap_selftest (error))
    goto out;

  repo_path = g_file_new_for_path (opt_repo);
  repo = self->repo = ostree_repo_new (repo_path);
  if (!ostree_repo_open (repo, cancellable, error))
    goto out;

  treefile_path = g_file_new_for_path (argv[1]);

  if (opt_workdir)
    {
      self->workdir = g_file_new_for_path (opt_workdir);
    }
  else
    {
      g_autofree char *tmpd = NULL;

      if (!rpmostree_mkdtemp ("/var/tmp/rpm-ostree.XXXXXX", &tmpd, NULL, error))
        goto out;

      self->workdir = g_file_new_for_path (tmpd);
      workdir_is_tmp = TRUE;

      if (opt_workdir_tmpfs)
        {
          if (mount ("tmpfs", tmpd, "tmpfs", 0, (const void*)"mode=755") != 0)
            {
              _rpmostree_set_prefix_error_from_errno (error, errno,
                                                      "mount(tmpfs): ");
              goto out;
            }
        }
    }

  if (!glnx_opendirat (AT_FDCWD, gs_file_get_path_cached (self->workdir),
                       FALSE, &self->workdir_dfd, error))
    goto out;

  if (opt_cachedir)
    {
      if (!glnx_opendirat (AT_FDCWD, opt_cachedir, TRUE, &self->cachedir_dfd, error))
        {
          g_prefix_error (error, "Opening cachedir '%s': ", opt_cachedir);
          goto out;
        }
    }
  else
    {
      self->cachedir_dfd = fcntl (self->workdir_dfd, F_DUPFD_CLOEXEC, 3);
      if (self->cachedir_dfd < 0)
        {
          glnx_set_error_from_errno (error);
          goto out;
        }
    }

  if (opt_metadata_strings)
    {
      if (!parse_keyvalue_strings (opt_metadata_strings,
                                   metadata_builder, error))
        goto out;
    }

  if (fchdir (self->workdir_dfd) != 0)
    {
      glnx_set_error_from_errno (error);
      goto out;
    }

  corectx = rpmostree_context_new_compose (self->cachedir_dfd, cancellable, error);
  if (!corectx)
    goto out;

  varsubsts = rpmostree_context_get_varsubsts (corectx);

  treefile_parser = json_parser_new ();
  if (!json_parser_load_from_file (treefile_parser,
                                   gs_file_get_path_cached (treefile_path),
                                   error))
    goto out;

  treefile_rootval = json_parser_get_root (treefile_parser);
  if (!JSON_NODE_HOLDS_OBJECT (treefile_rootval))
    {
      g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
                   "Treefile root is not an object");
      goto out;
    }
  treefile = json_node_get_object (treefile_rootval);

  if (!process_includes (self, treefile_path, 0, treefile,
                         cancellable, error))
    goto out;

  if (opt_print_only)
    {
      glnx_unref_object JsonGenerator *generator = json_generator_new ();
      g_autoptr(GOutputStream) stdout = g_unix_output_stream_new (1, FALSE);

      json_generator_set_pretty (generator, TRUE);
      json_generator_set_root (generator, treefile_rootval);
      (void) json_generator_to_stream (generator, stdout, NULL, NULL);

      exit_status = EXIT_SUCCESS;
      goto out;
    }

  { const char *input_ref = _rpmostree_jsonutil_object_require_string_member (treefile, "ref", error);
    if (!input_ref)
      goto out;
    self->ref = _rpmostree_varsubst_string (input_ref, varsubsts, error);
    if (!self->ref)
      goto out;
  }

  if (!ostree_repo_read_commit (repo, self->ref, &previous_root, &previous_checksum,
                                cancellable, &temp_error))
    {
      if (g_error_matches (temp_error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND))
        { 
          g_clear_error (&temp_error);
          g_print ("No previous commit for %s\n", self->ref);
        }
      else
        {
          g_propagate_error (error, temp_error);
          goto out;
        }
    }
  else
    g_print ("Previous commit: %s\n", previous_checksum);

  self->previous_checksum = previous_checksum;

  yumroot = g_file_get_child (self->workdir, "rootfs.tmp");
  if (!glnx_shutil_rm_rf_at (self->workdir_dfd, "rootfs.tmp", cancellable, error))
    goto out;

  if (json_object_has_member (treefile, "automatic_version_prefix") &&
      !compose_strv_contains_prefix (opt_metadata_strings, "version="))
    {
      g_autoptr(GVariant) variant = NULL;
      g_autofree char *last_version = NULL;
      const char *ver_prefix;

      ver_prefix = _rpmostree_jsonutil_object_require_string_member (treefile,
                                                                     "automatic_version_prefix",
                                                                     error);
      if (!ver_prefix)
          goto out;

      if (previous_checksum)
        {
          if (!ostree_repo_load_variant (repo, OSTREE_OBJECT_TYPE_COMMIT,
                                         previous_checksum, &variant, error))
            goto out;

          last_version = checksum_version (variant);
        }

      next_version = _rpmostree_util_next_version (ver_prefix, last_version);
      g_variant_builder_add (metadata_builder, "{sv}", "version",
                             g_variant_new_string (next_version));
    }

  bootstrap_packages = g_ptr_array_new ();
  packages = g_ptr_array_new ();

  if (json_object_has_member (treefile, "bootstrap_packages"))
    {
      if (!_rpmostree_jsonutil_append_string_array_to (treefile, "bootstrap_packages", packages, error))
        goto out;
    }
  if (!_rpmostree_jsonutil_append_string_array_to (treefile, "packages", packages, error))
    goto out;

  { g_autofree char *thisarch_packages = g_strconcat ("packages-", dnf_context_get_base_arch (rpmostree_context_get_hif (corectx)), NULL);

    if (json_object_has_member (treefile, thisarch_packages))
      {
        if (!_rpmostree_jsonutil_append_string_array_to (treefile, thisarch_packages, packages, error))
          goto out;
      }
  }
  g_ptr_array_add (packages, NULL);

  { glnx_unref_object JsonGenerator *generator = json_generator_new ();
    char *treefile_buf = NULL;
    gsize len;

    json_generator_set_root (generator, treefile_rootval);
    json_generator_set_pretty (generator, TRUE);
    treefile_buf = json_generator_to_data (generator, &len);

    self->serialized_treefile = g_bytes_new_take (treefile_buf, len);
  }

  treefile_dirpath = g_file_get_parent (treefile_path);
  if (TRUE)
    {
      gboolean generate_from_previous = TRUE;

      if (!_rpmostree_jsonutil_object_get_optional_boolean_member (treefile,
                                                                   "preserve-passwd",
                                                                   &generate_from_previous,
                                                                   error))
        goto out;

      if (generate_from_previous)
        {
          if (!rpmostree_generate_passwd_from_previous (repo, yumroot,
                                                        treefile_dirpath,
                                                        previous_root, treefile,
                                                        cancellable, error))
            goto out;
        }
    }

  { gboolean unmodified = FALSE;

    if (!install_packages_in_root (self, corectx, treefile, yumroot,
                                   (char**)packages->pdata,
                                   opt_force_nocache ? NULL : &unmodified,
                                   &new_inputhash,
                                   cancellable, error))
      goto out;

    if (unmodified)
      {
        g_print ("No apparent changes since previous commit; use --force-nocache to override\n");
        exit_status = EXIT_SUCCESS;
        goto out;
      }
    else if (opt_dry_run)
      {
        g_print ("--dry-run complete, exiting\n");
        exit_status = EXIT_SUCCESS;
        goto out;
      }
  }

  if (g_strcmp0 (g_getenv ("RPM_OSTREE_BREAK"), "post-yum") == 0)
    goto out;

  if (!glnx_opendirat (AT_FDCWD, gs_file_get_path_cached (yumroot), TRUE,
                       &rootfs_fd, error))
    goto out;

  if (!rpmostree_treefile_postprocessing (rootfs_fd, self->treefile_context_dirs->pdata[0],
                                          self->serialized_treefile, treefile,
                                          next_version, cancellable, error))
    goto out;

  if (!rpmostree_prepare_rootfs_for_commit (yumroot, treefile, cancellable, error))
    goto out;

  /* Reopen since the prepare renamed */
  (void) close (rootfs_fd);
  if (!glnx_opendirat (AT_FDCWD, gs_file_get_path_cached (yumroot), TRUE,
                       &rootfs_fd, error))
    goto out;

  if (!rpmostree_copy_additional_files (yumroot, self->treefile_context_dirs->pdata[0], treefile, cancellable, error))
    goto out;

  if (!rpmostree_check_passwd (repo, yumroot, treefile_dirpath, treefile,
                               previous_checksum,
                               cancellable, error))
    goto out;

  if (!rpmostree_check_groups (repo, yumroot, treefile_dirpath, treefile,
                               previous_checksum,
                               cancellable, error))
    goto out;

  {
    const char *gpgkey;
    gboolean selinux = TRUE;
    g_autoptr(GVariant) metadata = NULL;

    g_variant_builder_add (metadata_builder, "{sv}",
                           "rpmostree.inputhash",
                           g_variant_new_string (new_inputhash));

    metadata = g_variant_ref_sink (g_variant_builder_end (metadata_builder));

    if (!_rpmostree_jsonutil_object_get_optional_string_member (treefile, "gpg_key", &gpgkey, error))
      goto out;

    if (!_rpmostree_jsonutil_object_get_optional_boolean_member (treefile,
                                                                 "selinux",
                                                                 &selinux,
                                                                 error))
      goto out;

    { g_autofree char *new_revision = NULL;

      if (!rpmostree_commit (rootfs_fd, repo, self->ref, metadata, gpgkey, selinux, NULL,
                             &new_revision,
                             cancellable, error))
        goto out;

      g_print ("%s => %s\n", self->ref, new_revision);

    }
  }

  if (opt_touch_if_changed)
    {
      gs_fd_close int fd = open (opt_touch_if_changed, O_CREAT|O_WRONLY|O_NOCTTY, 0644);
      if (fd == -1)
        {
          gs_set_error_from_errno (error, errno);
          g_prefix_error (error, "Updating '%s': ", opt_touch_if_changed);
          goto out;
        }
      if (futimens (fd, NULL) == -1)
        {
          gs_set_error_from_errno (error, errno);
          goto out;
        }
    }

  exit_status = EXIT_SUCCESS;

 out:
  /* Explicitly close this one now as it may have references to files
   * we delete below.
   */
  g_clear_object (&corectx);
  
  /* Move back out of the workding directory to ensure unmount works */
  (void )chdir ("/");

  if (self->workdir_dfd != -1)
    (void) close (self->workdir_dfd);

  if (workdir_is_tmp)
    {
      if (opt_workdir_tmpfs)
        if (umount (gs_file_get_path_cached (self->workdir)) != 0)
          {
            fprintf (stderr, "warning: umount failed: %m\n");
          }
      (void) gs_shutil_rm_rf (self->workdir, NULL, NULL);
    }
  if (self)
    {
      g_clear_object (&self->workdir);
      g_clear_pointer (&self->serialized_treefile, g_bytes_unref);
      g_ptr_array_unref (self->treefile_context_dirs);
    }

  return exit_status;
}