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); }
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; }
/* 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; }
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; }
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; }
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; }
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; }
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; }
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; }
/** * 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; }
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; }
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, ¤t_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; }
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; }
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; }
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); }
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; }
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; }