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); }
gboolean ostree_builtin_checkout (int argc, char **argv, GCancellable *cancellable, GError **error) { GOptionContext *context; glnx_unref_object OstreeRepo *repo = NULL; gboolean ret = FALSE; const char *commit; const char *destination; g_autofree char *resolved_commit = NULL; context = g_option_context_new ("COMMIT [DESTINATION] - Check out a commit into a filesystem tree"); if (!ostree_option_context_parse (context, options, &argc, &argv, OSTREE_BUILTIN_FLAG_NONE, &repo, cancellable, error)) goto out; if (opt_disable_fsync) ostree_repo_set_disable_fsync (repo, TRUE); if (argc < 2) { gchar *help = g_option_context_get_help (context, TRUE, NULL); g_printerr ("%s\n", help); g_free (help); g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED, "COMMIT must be specified"); goto out; } if (opt_from_stdin || opt_from_file) { destination = argv[1]; if (!process_many_checkouts (repo, destination, cancellable, error)) goto out; } else { commit = argv[1]; if (argc < 3) destination = commit; else destination = argv[2]; if (!ostree_repo_resolve_rev (repo, commit, FALSE, &resolved_commit, error)) goto out; if (!process_one_checkout (repo, resolved_commit, opt_subpath, destination, cancellable, error)) goto out; } ret = TRUE; out: if (context) g_option_context_free (context); return ret; }
gboolean ostree_builtin_reset (int argc, char **argv, OstreeCommandInvocation *invocation, GCancellable *cancellable, GError **error) { g_autoptr(GOptionContext) context = NULL; g_autoptr(OstreeRepo) repo = NULL; g_autoptr(GHashTable) known_refs = NULL; gboolean ret = FALSE; const char *ref; const char *target = NULL; g_autofree char *checksum = NULL; /* FIXME: Add support for collection–refs. */ context = g_option_context_new ("REF COMMIT"); if (!ostree_option_context_parse (context, options, &argc, &argv, invocation, &repo, cancellable, error)) goto out; if (!ostree_ensure_repo_writable (repo, error)) goto out; if (argc <= 2) { ot_util_usage_error (context, "A REF and COMMIT argument is required", error); goto out; } ref = argv[1]; target = argv[2]; if (!ostree_repo_list_refs (repo, NULL, &known_refs, cancellable, error)) goto out; if (!g_hash_table_contains (known_refs, ref)) { g_set_error (error, G_IO_ERROR, G_IO_ERROR, "Invalid ref '%s'", ref); goto out; } if (!ostree_repo_resolve_rev (repo, target, FALSE, &checksum, error)) goto out; if (!ostree_repo_prepare_transaction (repo, NULL, cancellable, error)) goto out; ostree_repo_transaction_set_ref (repo, NULL, ref, checksum); if (!ostree_repo_commit_transaction (repo, NULL, cancellable, error)) goto out; ret = TRUE; out: if (repo) ostree_repo_abort_transaction (repo, cancellable, NULL); return ret; }
/** * rpmostreed_repo_lookup_cached_version: * @repo: Repo * @refspec: Repository branch * @version: Version to look for * @cancellable: Cancellable * @out_checksum: (out) (allow-none): Commit checksum, or %NULL * @error: Error * * Similar to rpmostreed_repo_lookup_version(), except without pulling * from a remote repository. It traverses whatever commits are available * locally in @repo. * * Returns: %TRUE on success, %FALSE on failure */ gboolean rpmostreed_repo_lookup_cached_version (OstreeRepo *repo, const char *refspec, const char *version, GCancellable *cancellable, char **out_checksum, GError **error) { VersionVisitorClosure closure = { version, NULL }; g_autofree char *checksum = NULL; gboolean ret = FALSE; g_return_val_if_fail (OSTREE_IS_REPO (repo), FALSE); g_return_val_if_fail (refspec != NULL, FALSE); g_return_val_if_fail (version != NULL, FALSE); if (!ostree_repo_resolve_rev (repo, refspec, FALSE, &checksum, error)) goto out; while (checksum != NULL) { g_autoptr(GVariant) commit = NULL; gboolean stop = FALSE; if (!ostree_repo_load_commit (repo, checksum, &commit, NULL, error)) goto out; if (!version_visitor (repo, checksum, commit, &closure, &stop, error)) goto out; g_clear_pointer (&checksum, g_free); if (!stop) checksum = ostree_commit_get_parent (commit); } if (closure.checksum == NULL) { g_set_error (error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND, "Version %s not cached in %s", version, refspec); goto out; } if (out_checksum != NULL) *out_checksum = g_steal_pointer (&closure.checksum); g_free (closure.checksum); ret = TRUE; out: return ret; }
struct RpmRevisionData * rpmrev_new (OstreeRepo *repo, const char *rev, const GPtrArray *patterns, GCancellable *cancellable, GError **error) { struct RpmRevisionData *rpmrev = NULL; g_autofree char *commit = NULL; g_autoptr(RpmOstreeRefTs) refts = NULL; if (!ostree_repo_resolve_rev (repo, rev, FALSE, &commit, error)) goto out; if (!rpmostree_get_refts_for_commit (repo, commit, &refts, cancellable, error)) goto out; rpmrev = g_malloc0 (sizeof(struct RpmRevisionData)); rpmrev->refts = g_steal_pointer (&refts); rpmrev->commit = g_steal_pointer (&commit); rpmrev->rpmdb = rpmhdrs_new (rpmrev->refts, patterns); out: return rpmrev; }
gboolean ostree_builtin_commit (int argc, char **argv, GCancellable *cancellable, GError **error) { GOptionContext *context; glnx_unref_object OstreeRepo *repo = NULL; gboolean ret = FALSE; gboolean skip_commit = FALSE; g_autoptr(GFile) object_to_commit = NULL; g_autofree char *parent = NULL; g_autofree char *commit_checksum = NULL; g_autoptr(GFile) root = NULL; g_autoptr(GVariant) metadata = NULL; g_autoptr(GVariant) detached_metadata = NULL; glnx_unref_object OstreeMutableTree *mtree = NULL; g_autofree char *tree_type = NULL; g_autoptr(GHashTable) mode_adds = NULL; OstreeRepoCommitModifierFlags flags = 0; OstreeRepoCommitModifier *modifier = NULL; OstreeRepoTransactionStats stats; context = g_option_context_new ("[PATH] - Commit a new revision"); if (!ostree_option_context_parse (context, options, &argc, &argv, OSTREE_BUILTIN_FLAG_NONE, &repo, cancellable, error)) goto out; if (!ostree_ensure_repo_writable (repo, error)) goto out; if (opt_statoverride_file) { if (!parse_statoverride_file (&mode_adds, cancellable, error)) goto out; } if (opt_metadata_strings) { if (!parse_keyvalue_strings (opt_metadata_strings, &metadata, error)) goto out; } if (opt_detached_metadata_strings) { if (!parse_keyvalue_strings (opt_detached_metadata_strings, &detached_metadata, error)) goto out; } if (!opt_branch) { g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED, "A branch must be specified with --branch"); goto out; } if (opt_no_xattrs) flags |= OSTREE_REPO_COMMIT_MODIFIER_FLAGS_SKIP_XATTRS; if (opt_generate_sizes) flags |= OSTREE_REPO_COMMIT_MODIFIER_FLAGS_GENERATE_SIZES; if (opt_disable_fsync) ostree_repo_set_disable_fsync (repo, TRUE); if (flags != 0 || opt_owner_uid >= 0 || opt_owner_gid >= 0 || opt_statoverride_file != NULL || opt_no_xattrs) { modifier = ostree_repo_commit_modifier_new (flags, commit_filter, mode_adds, NULL); } if (!ostree_repo_resolve_rev (repo, opt_branch, TRUE, &parent, error)) goto out; if (!opt_subject && !opt_body) { if (!commit_editor (repo, opt_branch, &opt_subject, &opt_body, cancellable, error)) goto out; } if (!opt_subject) { g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED, "A subject must be specified with --subject"); goto out; } if (!ostree_repo_prepare_transaction (repo, NULL, cancellable, error)) goto out; if (opt_link_checkout_speedup && !ostree_repo_scan_hardlinks (repo, cancellable, error)) goto out; mtree = ostree_mutable_tree_new (); if (argc <= 1 && (opt_trees == NULL || opt_trees[0] == NULL)) { char *current_dir = g_get_current_dir (); object_to_commit = g_file_new_for_path (current_dir); g_free (current_dir); if (!ostree_repo_write_directory_to_mtree (repo, object_to_commit, mtree, modifier, cancellable, error)) goto out; } else if (opt_trees != NULL) { const char *const*tree_iter; const char *tree; const char *eq; for (tree_iter = (const char *const*)opt_trees; *tree_iter; tree_iter++) { tree = *tree_iter; eq = strchr (tree, '='); if (!eq) { g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "Missing type in tree specification '%s'", tree); goto out; } g_free (tree_type); tree_type = g_strndup (tree, eq - tree); tree = eq + 1; g_clear_object (&object_to_commit); if (strcmp (tree_type, "dir") == 0) { object_to_commit = g_file_new_for_path (tree); if (!ostree_repo_write_directory_to_mtree (repo, object_to_commit, mtree, modifier, cancellable, error)) goto out; } else if (strcmp (tree_type, "tar") == 0) { object_to_commit = g_file_new_for_path (tree); if (!ostree_repo_write_archive_to_mtree (repo, object_to_commit, mtree, modifier, opt_tar_autocreate_parents, cancellable, error)) goto out; } else if (strcmp (tree_type, "ref") == 0) { if (!ostree_repo_read_commit (repo, tree, &object_to_commit, NULL, cancellable, error)) goto out; if (!ostree_repo_write_directory_to_mtree (repo, object_to_commit, mtree, modifier, cancellable, error)) goto out; } else { g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "Invalid tree type specification '%s'", tree_type); goto out; } } } else { g_assert (argc > 1); object_to_commit = g_file_new_for_path (argv[1]); if (!ostree_repo_write_directory_to_mtree (repo, object_to_commit, mtree, modifier, cancellable, error)) goto out; } if (mode_adds && g_hash_table_size (mode_adds) > 0) { GHashTableIter hash_iter; gpointer key, value; g_hash_table_iter_init (&hash_iter, mode_adds); while (g_hash_table_iter_next (&hash_iter, &key, &value)) { g_printerr ("Unmatched statoverride path: %s\n", (char*)key); } g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "Unmatched statoverride paths"); goto out; } if (!ostree_repo_write_mtree (repo, mtree, &root, cancellable, error)) goto out; if (opt_skip_if_unchanged && parent) { g_autoptr(GFile) parent_root; if (!ostree_repo_read_commit (repo, parent, &parent_root, NULL, cancellable, error)) goto out; if (g_file_equal (root, parent_root)) skip_commit = TRUE; } if (!skip_commit) { gboolean update_summary; if (!ostree_repo_write_commit (repo, parent, opt_subject, opt_body, metadata, OSTREE_REPO_FILE (root), &commit_checksum, cancellable, error)) goto out; if (detached_metadata) { if (!ostree_repo_write_commit_detached_metadata (repo, commit_checksum, detached_metadata, cancellable, error)) goto out; } if (opt_key_ids) { char **iter; for (iter = opt_key_ids; iter && *iter; iter++) { const char *keyid = *iter; if (!ostree_repo_sign_commit (repo, commit_checksum, keyid, opt_gpg_homedir, cancellable, error)) goto out; } } ostree_repo_transaction_set_ref (repo, NULL, opt_branch, commit_checksum); if (!ostree_repo_commit_transaction (repo, &stats, cancellable, error)) goto out; /* The default for this option is FALSE, even for archive-z2 repos, * because ostree supports multiple processes committing to the same * repo (but different refs) concurrently, and in fact gnome-continuous * actually does this. In that context it's best to update the summary * explicitly instead of automatically here. */ if (!ot_keyfile_get_boolean_with_default (ostree_repo_get_config (repo), "core", "commit-update-summary", FALSE, &update_summary, error)) goto out; if (update_summary && !ostree_repo_regenerate_summary (repo, NULL, cancellable, error)) goto out; } else { commit_checksum = g_strdup (parent); } if (opt_table_output) { g_print ("Commit: %s\n", commit_checksum); g_print ("Metadata Total: %u\n", stats.metadata_objects_total); g_print ("Metadata Written: %u\n", stats.metadata_objects_written); g_print ("Content Total: %u\n", stats.content_objects_total); g_print ("Content Written: %u\n", stats.content_objects_written); g_print ("Content Bytes Written: %" G_GUINT64_FORMAT "\n", stats.content_bytes_written); } else { g_print ("%s\n", commit_checksum); } ret = TRUE; out: if (repo) ostree_repo_abort_transaction (repo, cancellable, NULL); if (context) g_option_context_free (context); if (modifier) ostree_repo_commit_modifier_unref (modifier); return ret; }
/** * 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; }
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; }
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_show (int argc, char **argv, OstreeRepo *repo, GCancellable *cancellable, GError **error) { GOptionContext *context; gboolean ret = FALSE; const char *rev; gs_free char *resolved_rev = NULL; context = g_option_context_new ("OBJECT - Output a metadata object"); g_option_context_add_main_entries (context, options, NULL); if (!g_option_context_parse (context, &argc, &argv, error)) goto out; if (argc <= 1) { ot_util_usage_error (context, "An object argument is required", error); goto out; } rev = argv[1]; if (opt_print_metadata_key || opt_print_detached_metadata_key) { gboolean detached = opt_print_detached_metadata_key != NULL; const char *key = detached ? opt_print_detached_metadata_key : opt_print_metadata_key; if (!ostree_repo_resolve_rev (repo, rev, FALSE, &resolved_rev, error)) goto out; if (!do_print_metadata_key (repo, resolved_rev, detached, key, error)) goto out; } else if (opt_print_related) { if (!ostree_repo_resolve_rev (repo, rev, FALSE, &resolved_rev, error)) goto out; if (!do_print_related (repo, rev, resolved_rev, error)) goto out; } else if (opt_print_variant_type) { if (!do_print_variant_generic (G_VARIANT_TYPE (opt_print_variant_type), rev, error)) goto out; } else { gboolean found = FALSE; if (!ostree_validate_checksum_string (rev, NULL)) { if (!ostree_repo_resolve_rev (repo, rev, FALSE, &resolved_rev, error)) goto out; if (!print_object (repo, OSTREE_OBJECT_TYPE_COMMIT, resolved_rev, error)) goto out; } else { if (!print_if_found (repo, OSTREE_OBJECT_TYPE_COMMIT, rev, &found, cancellable, error)) goto out; if (!print_if_found (repo, OSTREE_OBJECT_TYPE_DIR_META, rev, &found, cancellable, error)) goto out; if (!print_if_found (repo, OSTREE_OBJECT_TYPE_DIR_TREE, rev, &found, cancellable, error)) goto out; if (!found) { gs_unref_object GFileInfo *finfo = NULL; gs_unref_variant GVariant *xattrs = NULL; GFileType filetype; if (!ostree_repo_load_file (repo, resolved_rev, NULL, &finfo, &xattrs, cancellable, error)) goto out; g_print ("Object: %s\nType: %s\n", rev, ostree_object_type_to_string (OSTREE_OBJECT_TYPE_FILE)); filetype = g_file_info_get_file_type (finfo); g_print ("File Type: "); switch (filetype) { case G_FILE_TYPE_REGULAR: g_print ("regular\n"); g_print ("Size: %" G_GUINT64_FORMAT "\n", g_file_info_get_size (finfo)); break; case G_FILE_TYPE_SYMBOLIC_LINK: g_print ("symlink\n"); g_print ("Target: %s\n", g_file_info_get_symlink_target (finfo)); break; default: g_printerr ("(unknown type %u)\n", (guint)filetype); } g_print ("Mode: 0%04o\n", g_file_info_get_attribute_uint32 (finfo, "unix::mode")); g_print ("Uid: %u\n", g_file_info_get_attribute_uint32 (finfo, "unix::uid")); g_print ("Gid: %u\n", g_file_info_get_attribute_uint32 (finfo, "unix::gid")); g_print ("Extended Attributes: "); if (xattrs) { gs_free char *xattr_string = g_variant_print (xattrs, TRUE); g_print ("{ %s }\n", xattr_string); } else { g_print ("(none)\n"); } } } } ret = TRUE; out: if (context) g_option_context_free (context); return ret; }
gboolean flatpak_builtin_build_sign (int argc, char **argv, GCancellable *cancellable, GError **error) { g_autoptr(GOptionContext) context = NULL; g_autoptr(GFile) repofile = NULL; g_autoptr(OstreeRepo) repo = NULL; const char *location; const char *branch; const char *id; g_autofree char *commit_checksum = NULL; g_autofree char *ref = NULL; char **iter; context = g_option_context_new ("LOCATION ID [BRANCH] - Create a repository from a build directory"); if (!flatpak_option_context_parse (context, options, &argc, &argv, FLATPAK_BUILTIN_FLAG_NO_DIR, NULL, cancellable, error)) return FALSE; if (argc < 3) { usage_error (context, "LOCATION and DIRECTORY must be specified", error); return FALSE; } location = argv[1]; id = argv[2]; if (argc >= 4) branch = argv[3]; else branch = "master"; if (!flatpak_is_valid_name (id)) return flatpak_fail (error, "'%s' is not a valid name", id); if (!flatpak_is_valid_branch (branch)) return flatpak_fail (error, "'%s' is not a valid branch name", branch); if (opt_gpg_key_ids == NULL) return flatpak_fail (error, "No gpg key ids specified"); if (opt_runtime) ref = flatpak_build_runtime_ref (id, branch, opt_arch); else ref = flatpak_build_app_ref (id, branch, opt_arch); repofile = g_file_new_for_commandline_arg (location); repo = ostree_repo_new (repofile); if (!ostree_repo_open (repo, cancellable, error)) return FALSE; if (!ostree_repo_resolve_rev (repo, ref, TRUE, &commit_checksum, error)) return FALSE; for (iter = opt_gpg_key_ids; iter && *iter; iter++) { const char *keyid = *iter; if (!ostree_repo_sign_commit (repo, commit_checksum, keyid, opt_gpg_homedir, cancellable, error)) return FALSE; } return TRUE; }
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; }
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_diff (int argc, char **argv, OstreeCommandInvocation *invocation, GCancellable *cancellable, GError **error) { gboolean ret = FALSE; g_autoptr(GOptionContext) context = NULL; g_autoptr(OstreeRepo) repo = NULL; const char *src; const char *target; g_autofree char *src_prev = NULL; g_autoptr(GFile) srcf = NULL; g_autoptr(GFile) targetf = NULL; g_autoptr(GPtrArray) modified = NULL; g_autoptr(GPtrArray) removed = NULL; g_autoptr(GPtrArray) added = NULL; context = g_option_context_new ("REV TARGETDIR"); if (!ostree_option_context_parse (context, options, &argc, &argv, invocation, &repo, cancellable, error)) goto out; if (argc < 2) { gchar *help = g_option_context_get_help (context, TRUE, NULL); g_printerr ("%s\n", help); g_free (help); g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED, "REV must be specified"); goto out; } if (argc == 2) { src_prev = g_strconcat (argv[1], "^", NULL); src = src_prev; target = argv[1]; } else { src = argv[1]; target = argv[2]; } if (!opt_stats && !opt_fs_diff) opt_fs_diff = TRUE; if (opt_fs_diff) { OstreeDiffFlags diff_flags = OSTREE_DIFF_FLAGS_NONE; if (opt_no_xattrs) diff_flags |= OSTREE_DIFF_FLAGS_IGNORE_XATTRS; if (!parse_file_or_commit (repo, src, &srcf, cancellable, error)) goto out; if (!parse_file_or_commit (repo, target, &targetf, cancellable, error)) goto out; modified = g_ptr_array_new_with_free_func ((GDestroyNotify)ostree_diff_item_unref); removed = g_ptr_array_new_with_free_func ((GDestroyNotify)g_object_unref); added = g_ptr_array_new_with_free_func ((GDestroyNotify)g_object_unref); OstreeDiffDirsOptions diff_opts = { opt_owner_uid, opt_owner_gid }; if (!ostree_diff_dirs_with_options (diff_flags, srcf, targetf, modified, removed, added, &diff_opts, cancellable, error)) goto out; ostree_diff_print (srcf, targetf, modified, removed, added); } if (opt_stats) { g_autoptr(GHashTable) reachable_a = NULL; g_autoptr(GHashTable) reachable_b = NULL; g_autoptr(GHashTable) reachable_intersection = NULL; g_autofree char *rev_a = NULL; g_autofree char *rev_b = NULL; g_autofree char *size = NULL; guint a_size; guint b_size; guint64 total_common; if (!ostree_repo_resolve_rev (repo, src, FALSE, &rev_a, error)) goto out; if (!ostree_repo_resolve_rev (repo, target, FALSE, &rev_b, error)) goto out; if (!ostree_repo_traverse_commit (repo, rev_a, 0, &reachable_a, cancellable, error)) goto out; if (!ostree_repo_traverse_commit (repo, rev_b, 0, &reachable_b, cancellable, error)) goto out; a_size = g_hash_table_size (reachable_a); b_size = g_hash_table_size (reachable_b); g_print ("[A] Object Count: %u\n", a_size); g_print ("[B] Object Count: %u\n", b_size); reachable_intersection = reachable_set_intersect (reachable_a, reachable_b); g_print ("Common Object Count: %u\n", g_hash_table_size (reachable_intersection)); if (!object_set_total_size (repo, reachable_intersection, &total_common, cancellable, error)) goto out; size = g_format_size_full (total_common, 0); g_print ("Common Object Size: %s\n", size); } ret = TRUE; out: return ret; }
/** * rpmostreed_repo_pull_ancestry: * @repo: Repo * @refspec: Repository branch * @visitor: (allow-none): Visitor function to call on each commit * @visitor_data: (allow-none): User data for @visitor * @progress: (allow-none): Progress * @cancellable: Cancellable * @error: Error * * Downloads an ancestry of commit objects starting from @refspec. * * If a @visitor function pointer is given, commit objects are downloaded * in batches and the @visitor function is called for each commit object. * The @visitor function can stop the recursion, such as when looking for * a particular commit. * * Returns: %TRUE on success, %FALSE on failure */ gboolean rpmostreed_repo_pull_ancestry (OstreeRepo *repo, const char *refspec, RpmostreedCommitVisitor visitor, gpointer visitor_data, OstreeAsyncProgress *progress, GCancellable *cancellable, GError **error) { OstreeRepoPullFlags flags; GVariantDict options; GVariant *refs_value; const char *refs_array[] = { NULL, NULL }; g_autofree char *remote = NULL; g_autofree char *ref = NULL; g_autofree char *checksum = NULL; int depth, ii; gboolean ret = FALSE; g_return_val_if_fail (OSTREE_IS_REPO (repo), FALSE); g_return_val_if_fail (refspec != NULL, FALSE); if (!ostree_parse_refspec (refspec, &remote, &ref, error)) goto out; /* If no visitor function was provided then we won't be short-circuiting * the recursion, so pull everything in one shot. Otherwise pull commits * in increasingly large batches. */ depth = (visitor != NULL) ? 10 : -1; flags = OSTREE_REPO_PULL_FLAGS_COMMIT_ONLY; /* It's important to use the ref name instead of a checksum on the first * pass because we want to search from the latest available commit on the * remote server, which is not necessarily what the ref name is currently * pointing at in our local repo. */ refs_array[0] = ref; while (TRUE) { /* Floating reference, transferred to dictionary. */ refs_value = g_variant_new_strv ((const char * const *) refs_array, -1); g_variant_dict_init (&options, NULL); g_variant_dict_insert (&options, "depth", "i", depth); g_variant_dict_insert (&options, "flags", "i", flags); g_variant_dict_insert_value (&options, "refs", refs_value); if (!ostree_repo_pull_with_options (repo, remote, g_variant_dict_end (&options), progress, cancellable, error)) goto out; /* First pass only. Now we can resolve the ref to a checksum. */ if (checksum == NULL) { if (!ostree_repo_resolve_rev (repo, ref, FALSE, &checksum, error)) goto out; } /* If depth is negative (no visitor), this loop is skipped. */ for (ii = 0; ii < depth && checksum != NULL; ii++) { g_autoptr(GVariant) commit = NULL; gboolean stop = FALSE; if (!ostree_repo_load_commit (repo, checksum, &commit, NULL, error)) goto out; if (!visitor (repo, checksum, commit, visitor_data, &stop, error)) goto out; g_clear_pointer (&checksum, g_free); if (!stop) checksum = ostree_commit_get_parent (commit); } /* Break if no visitor, or visitor told us to stop. */ if (depth < 0 || checksum == NULL) break; /* Pull the next batch of commits, twice as many. */ refs_array[0] = checksum; depth = depth * 2; } ret = TRUE; out: return ret; }
gboolean ot_admin_builtin_deploy (int argc, char **argv, OstreeSysroot *sysroot, GCancellable *cancellable, GError **error) { gboolean ret = FALSE; const char *refspec; GOptionContext *context; GKeyFile *origin = NULL; gs_unref_object OstreeRepo *repo = NULL; gs_unref_ptrarray GPtrArray *new_deployments = NULL; gs_unref_object OstreeDeployment *new_deployment = NULL; gs_unref_object OstreeDeployment *merge_deployment = NULL; gs_free char *revision = NULL; __attribute__((cleanup(_ostree_kernel_args_cleanup))) OstreeKernelArgs *kargs = NULL; context = g_option_context_new ("REFSPEC - Checkout revision REFSPEC as the new default deployment"); g_option_context_add_main_entries (context, options, NULL); if (!g_option_context_parse (context, &argc, &argv, error)) goto out; if (argc < 2) { ot_util_usage_error (context, "REF/REV must be specified", error); goto out; } refspec = argv[1]; if (!ostree_sysroot_load (sysroot, cancellable, error)) goto out; if (!ostree_sysroot_get_repo (sysroot, &repo, cancellable, error)) goto out; /* Find the currently booted deployment, if any; we will ensure it * is present in the new deployment list. */ if (!ot_admin_require_booted_deployment_or_osname (sysroot, opt_osname, cancellable, error)) { g_prefix_error (error, "Looking for booted deployment: "); goto out; } if (opt_origin_path) { origin = g_key_file_new (); if (!g_key_file_load_from_file (origin, opt_origin_path, 0, error)) goto out; } else { origin = ostree_sysroot_origin_new_from_refspec (sysroot, refspec); } if (!ostree_repo_resolve_rev (repo, refspec, FALSE, &revision, error)) goto out; merge_deployment = ostree_sysroot_get_merge_deployment (sysroot, opt_osname); /* Here we perform cleanup of any leftover data from previous * partial failures. This avoids having to call gs_shutil_rm_rf() * at random points throughout the process. * * TODO: Add /ostree/transaction file, and only do this cleanup if * we find it. */ if (!ostree_sysroot_cleanup (sysroot, cancellable, error)) { g_prefix_error (error, "Performing initial cleanup: "); goto out; } kargs = _ostree_kernel_args_new (); /* If they want the current kernel's args, they very likely don't * want the ones from the merge. */ if (opt_kernel_proc_cmdline) { gs_unref_object GFile *proc_cmdline_path = g_file_new_for_path ("/proc/cmdline"); gs_free char *proc_cmdline = NULL; gsize proc_cmdline_len = 0; gs_strfreev char **proc_cmdline_args = NULL; if (!g_file_load_contents (proc_cmdline_path, cancellable, &proc_cmdline, &proc_cmdline_len, NULL, error)) goto out; g_strchomp (proc_cmdline); proc_cmdline_args = g_strsplit (proc_cmdline, " ", -1); _ostree_kernel_args_replace_argv (kargs, proc_cmdline_args); } else if (merge_deployment) { OstreeBootconfigParser *bootconfig = ostree_deployment_get_bootconfig (merge_deployment); gs_strfreev char **previous_args = g_strsplit (ostree_bootconfig_parser_get (bootconfig, "options"), " ", -1); _ostree_kernel_args_replace_argv (kargs, previous_args); } if (opt_kernel_argv) { _ostree_kernel_args_replace_argv (kargs, opt_kernel_argv); } if (opt_kernel_argv_append) { _ostree_kernel_args_append_argv (kargs, opt_kernel_argv_append); } { gs_strfreev char **kargs_strv = _ostree_kernel_args_to_strv (kargs); if (!ostree_sysroot_deploy_tree (sysroot, opt_osname, revision, origin, merge_deployment, kargs_strv, &new_deployment, cancellable, error)) goto out; } if (!ostree_sysroot_simple_write_deployment (sysroot, opt_osname, new_deployment, merge_deployment, opt_retain ? OSTREE_SYSROOT_SIMPLE_WRITE_DEPLOYMENT_FLAGS_RETAIN : 0, cancellable, error)) goto out; ret = TRUE; out: if (origin) g_key_file_unref (origin); if (context) g_option_context_free (context); return ret; }
gboolean ostree_builtin_diff (int argc, char **argv, OstreeRepo *repo, GCancellable *cancellable, GError **error) { gboolean ret = FALSE; GOptionContext *context; const char *src; const char *target; gs_free char *src_prev = NULL; gs_unref_object GFile *srcf = NULL; gs_unref_object GFile *targetf = NULL; gs_unref_ptrarray GPtrArray *modified = NULL; gs_unref_ptrarray GPtrArray *removed = NULL; gs_unref_ptrarray GPtrArray *added = NULL; context = g_option_context_new ("REV TARGETDIR - Compare directory TARGETDIR against revision REV"); g_option_context_add_main_entries (context, options, NULL); if (!g_option_context_parse (context, &argc, &argv, error)) goto out; if (argc < 2) { gchar *help = g_option_context_get_help (context, TRUE, NULL); g_printerr ("%s\n", help); g_free (help); g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED, "REV must be specified"); goto out; } if (argc == 2) { src_prev = g_strconcat (argv[1], "^", NULL); src = src_prev; target = argv[1]; } else { src = argv[1]; target = argv[2]; } if (!opt_stats && !opt_fs_diff) opt_fs_diff = TRUE; if (opt_fs_diff) { if (!parse_file_or_commit (repo, src, &srcf, cancellable, error)) goto out; if (!parse_file_or_commit (repo, target, &targetf, cancellable, error)) goto out; modified = g_ptr_array_new_with_free_func ((GDestroyNotify)ostree_diff_item_unref); removed = g_ptr_array_new_with_free_func ((GDestroyNotify)g_object_unref); added = g_ptr_array_new_with_free_func ((GDestroyNotify)g_object_unref); if (!ostree_diff_dirs (OSTREE_DIFF_FLAGS_NONE, srcf, targetf, modified, removed, added, cancellable, error)) goto out; ostree_diff_print (srcf, targetf, modified, removed, added); } if (opt_stats) { gs_unref_hashtable GHashTable *reachable_a = NULL; gs_unref_hashtable GHashTable *reachable_b = NULL; gs_unref_hashtable GHashTable *reachable_intersection = NULL; gs_free char *rev_a = NULL; gs_free char *rev_b = NULL; gs_free char *size = NULL; guint a_size; guint b_size; guint64 total_common; if (!ostree_repo_resolve_rev (repo, src, FALSE, &rev_a, error)) goto out; if (!ostree_repo_resolve_rev (repo, target, FALSE, &rev_b, error)) goto out; if (!ostree_repo_traverse_commit (repo, rev_a, -1, &reachable_a, cancellable, error)) goto out; if (!ostree_repo_traverse_commit (repo, rev_b, -1, &reachable_b, cancellable, error)) goto out; a_size = g_hash_table_size (reachable_a); b_size = g_hash_table_size (reachable_b); g_print ("[A] Object Count: %u\n", a_size); g_print ("[B] Object Count: %u\n", b_size); reachable_intersection = reachable_set_intersect (reachable_a, reachable_b); g_print ("Common Object Count: %u\n", g_hash_table_size (reachable_intersection)); if (!object_set_total_size (repo, reachable_intersection, &total_common, cancellable, error)) goto out; size = g_format_size_full (total_common, 0); g_print ("Common Object Size: %s\n", size); } ret = TRUE; out: if (context) g_option_context_free (context); return ret; }
static gboolean ot_static_delta_builtin_generate (int argc, char **argv, GCancellable *cancellable, GError **error) { gboolean ret = FALSE; GOptionContext *context; glnx_unref_object OstreeRepo *repo = NULL; context = g_option_context_new ("Generate static delta files"); if (!ostree_option_context_parse (context, generate_options, &argc, &argv, OSTREE_BUILTIN_FLAG_NONE, &repo, cancellable, error)) goto out; if (!ostree_ensure_repo_writable (repo, error)) goto out; if (argc >= 3 && opt_to_rev == NULL) opt_to_rev = argv[2]; if (argc < 3 && opt_to_rev == NULL) { g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED, "TO revision must be specified"); goto out; } else { const char *from_source; g_autofree char *from_resolved = NULL; g_autofree char *to_resolved = NULL; g_autofree char *from_parent_str = NULL; g_autoptr(GVariantBuilder) parambuilder = NULL; g_assert (opt_to_rev); if (opt_empty) { if (opt_from_rev) { g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED, "Cannot specify both --empty and --from=REV"); goto out; } from_source = NULL; } else if (opt_from_rev == NULL) { from_parent_str = g_strconcat (opt_to_rev, "^", NULL); from_source = from_parent_str; } else { from_source = opt_from_rev; } if (from_source) { if (!ostree_repo_resolve_rev (repo, from_source, FALSE, &from_resolved, error)) goto out; } if (!ostree_repo_resolve_rev (repo, opt_to_rev, FALSE, &to_resolved, error)) goto out; parambuilder = g_variant_builder_new (G_VARIANT_TYPE ("a{sv}")); if (opt_min_fallback_size) g_variant_builder_add (parambuilder, "{sv}", "min-fallback-size", g_variant_new_uint32 (g_ascii_strtoull (opt_min_fallback_size, NULL, 10))); if (opt_max_bsdiff_size) g_variant_builder_add (parambuilder, "{sv}", "max-bsdiff-size", g_variant_new_uint32 (g_ascii_strtoull (opt_max_bsdiff_size, NULL, 10))); if (opt_max_chunk_size) g_variant_builder_add (parambuilder, "{sv}", "max-chunk-size", g_variant_new_uint32 (g_ascii_strtoull (opt_max_chunk_size, NULL, 10))); if (opt_disable_bsdiff) g_variant_builder_add (parambuilder, "{sv}", "bsdiff-enabled", g_variant_new_boolean (FALSE)); g_variant_builder_add (parambuilder, "{sv}", "verbose", g_variant_new_boolean (TRUE)); g_print ("Generating static delta:\n"); g_print (" From: %s\n", from_resolved ? from_resolved : "empty"); g_print (" To: %s\n", to_resolved); if (!ostree_repo_static_delta_generate (repo, OSTREE_STATIC_DELTA_GENERATE_OPT_MAJOR, from_resolved, to_resolved, NULL, g_variant_builder_end (parambuilder), cancellable, error)) goto out; } ret = TRUE; out: if (context) g_option_context_free (context); return ret; }
static gboolean process_many_checkouts (OstreeRepo *repo, GFile *target, GCancellable *cancellable, GError **error) { gboolean ret = FALSE; gsize len; GError *temp_error = NULL; gs_unref_object GInputStream *instream = NULL; gs_unref_object GDataInputStream *datastream = NULL; gs_free char *revision = NULL; gs_free char *subpath = NULL; gs_free char *resolved_commit = NULL; if (opt_from_stdin) { instream = (GInputStream*)g_unix_input_stream_new (0, FALSE); } else { gs_unref_object GFile *f = g_file_new_for_path (opt_from_file); instream = (GInputStream*)g_file_read (f, cancellable, error); if (!instream) goto out; } datastream = g_data_input_stream_new (instream); while ((revision = g_data_input_stream_read_upto (datastream, "", 1, &len, cancellable, &temp_error)) != NULL) { if (revision[0] == '\0') break; /* Read the null byte */ (void) g_data_input_stream_read_byte (datastream, cancellable, NULL); g_free (subpath); subpath = g_data_input_stream_read_upto (datastream, "", 1, &len, cancellable, &temp_error); if (temp_error) { g_propagate_error (error, temp_error); goto out; } /* Read the null byte */ (void) g_data_input_stream_read_byte (datastream, cancellable, NULL); if (!ostree_repo_resolve_rev (repo, revision, FALSE, &resolved_commit, error)) goto out; if (!process_one_checkout (repo, resolved_commit, subpath, target, cancellable, error)) { g_prefix_error (error, "Processing tree %s: ", resolved_commit); goto out; } g_free (revision); } if (temp_error) { g_propagate_error (error, temp_error); goto out; } ret = TRUE; out: return ret; }
gboolean rpmostree_commit (GFile *rootfs, OstreeRepo *repo, const char *refname, GVariant *metadata, const char *gpg_keyid, gboolean enable_selinux, GCancellable *cancellable, GError **error) { gboolean ret = FALSE; gs_unref_object OstreeMutableTree *mtree = NULL; OstreeRepoCommitModifier *commit_modifier = NULL; gs_free char *parent_revision = NULL; gs_free char *new_revision = NULL; gs_unref_object GFile *root_tree = NULL; gs_unref_object OstreeSePolicy *sepolicy = NULL; gs_fd_close int rootfs_fd = -1; if (!gs_file_open_dir_fd (rootfs, &rootfs_fd, cancellable, error)) goto out; /* hardcode targeted policy for now */ if (enable_selinux) { if (!rpmostree_prepare_rootfs_get_sepolicy (rootfs_fd, ".", &sepolicy, cancellable, error)) goto out; } g_print ("Committing '%s' ...\n", gs_file_get_path_cached (rootfs)); if (!ostree_repo_prepare_transaction (repo, NULL, cancellable, error)) goto out; mtree = ostree_mutable_tree_new (); commit_modifier = ostree_repo_commit_modifier_new (0, NULL, NULL, NULL); ostree_repo_commit_modifier_set_xattr_callback (commit_modifier, read_xattrs_cb, NULL, GINT_TO_POINTER (rootfs_fd)); if (sepolicy) { const char *policy_name = ostree_sepolicy_get_name (sepolicy); g_print ("Labeling with SELinux policy '%s'\n", policy_name); ostree_repo_commit_modifier_set_sepolicy (commit_modifier, sepolicy); } if (!ostree_repo_write_directory_to_mtree (repo, rootfs, mtree, commit_modifier, cancellable, error)) goto out; if (!ostree_repo_write_mtree (repo, mtree, &root_tree, cancellable, error)) goto out; if (!ostree_repo_resolve_rev (repo, refname, TRUE, &parent_revision, error)) goto out; if (!ostree_repo_write_commit (repo, parent_revision, "", "", metadata, (OstreeRepoFile*)root_tree, &new_revision, cancellable, error)) goto out; if (gpg_keyid) { g_print ("Signing commit %s with key %s\n", new_revision, gpg_keyid); if (!ostree_repo_sign_commit (repo, new_revision, gpg_keyid, NULL, cancellable, error)) goto out; } ostree_repo_transaction_set_ref (repo, NULL, refname, new_revision); if (!ostree_repo_commit_transaction (repo, NULL, cancellable, error)) goto out; g_print ("%s => %s\n", refname, new_revision); if (!g_getenv ("RPM_OSTREE_PRESERVE_ROOTFS")) (void) gs_shutil_rm_rf (rootfs, NULL, NULL); else g_print ("Preserved %s\n", gs_file_get_path_cached (rootfs)); ret = TRUE; out: return ret; }
gboolean ostree_repo_pull (OstreeRepo *self, const char *remote_name, char **refs_to_fetch, OstreeRepoPullFlags flags, OstreeAsyncProgress *progress, GCancellable *cancellable, GError **error) { gboolean ret = FALSE; GHashTableIter hash_iter; gpointer key, value; gboolean tls_permissive = FALSE; OstreeFetcherConfigFlags fetcher_flags = 0; gs_free char *remote_key = NULL; gs_free char *path = NULL; gs_free char *baseurl = NULL; gs_free char *summary_data = NULL; gs_unref_hashtable GHashTable *requested_refs_to_fetch = NULL; gs_unref_hashtable GHashTable *updated_refs = NULL; gs_unref_hashtable GHashTable *commits_to_fetch = NULL; gs_free char *remote_mode_str = NULL; GSource *queue_src = NULL; OtPullData pull_data_real = { 0, }; OtPullData *pull_data = &pull_data_real; SoupURI *summary_uri = NULL; GKeyFile *config = NULL; GKeyFile *remote_config = NULL; char **configured_branches = NULL; guint64 bytes_transferred; guint64 start_time; guint64 end_time; pull_data->async_error = error; pull_data->main_context = g_main_context_ref_thread_default (); pull_data->loop = g_main_loop_new (pull_data->main_context, FALSE); pull_data->flags = flags; pull_data->repo = self; pull_data->progress = progress; pull_data->scanned_metadata = g_hash_table_new_full (ostree_hash_object_name, g_variant_equal, (GDestroyNotify)g_variant_unref, NULL); pull_data->requested_content = g_hash_table_new_full (g_str_hash, g_str_equal, (GDestroyNotify)g_free, NULL); pull_data->requested_metadata = g_hash_table_new_full (g_str_hash, g_str_equal, (GDestroyNotify)g_free, NULL); start_time = g_get_monotonic_time (); pull_data->remote_name = g_strdup (remote_name); config = ostree_repo_get_config (self); remote_key = g_strdup_printf ("remote \"%s\"", pull_data->remote_name); if (!repo_get_string_key_inherit (self, remote_key, "url", &baseurl, error)) goto out; pull_data->base_uri = soup_uri_new (baseurl); #ifdef HAVE_GPGME if (!ot_keyfile_get_boolean_with_default (config, remote_key, "gpg-verify", TRUE, &pull_data->gpg_verify, error)) goto out; #else pull_data->gpg_verify = FALSE; #endif if (!ot_keyfile_get_boolean_with_default (config, remote_key, "tls-permissive", FALSE, &tls_permissive, error)) goto out; if (tls_permissive) fetcher_flags |= OSTREE_FETCHER_FLAGS_TLS_PERMISSIVE; pull_data->fetcher = ostree_fetcher_new (pull_data->repo->tmp_dir, fetcher_flags); if (!pull_data->base_uri) { g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "Failed to parse url '%s'", baseurl); goto out; } if (!load_remote_repo_config (pull_data, &remote_config, cancellable, error)) goto out; if (!ot_keyfile_get_value_with_default (remote_config, "core", "mode", "bare", &remote_mode_str, error)) goto out; if (!ostree_repo_mode_from_string (remote_mode_str, &pull_data->remote_mode, error)) goto out; if (pull_data->remote_mode != OSTREE_REPO_MODE_ARCHIVE_Z2) { g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "Can't pull from archives with mode \"%s\"", remote_mode_str); goto out; } requested_refs_to_fetch = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free); updated_refs = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free); commits_to_fetch = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL); if (refs_to_fetch != NULL) { char **strviter; for (strviter = refs_to_fetch; *strviter; strviter++) { const char *branch = *strviter; char *contents; if (ostree_validate_checksum_string (branch, NULL)) { char *key = g_strdup (branch); g_hash_table_insert (commits_to_fetch, key, key); } else { if (!fetch_ref_contents (pull_data, branch, &contents, cancellable, error)) goto out; /* Transfer ownership of contents */ g_hash_table_insert (requested_refs_to_fetch, g_strdup (branch), contents); } } } else { GError *temp_error = NULL; gboolean fetch_all_refs; configured_branches = g_key_file_get_string_list (config, remote_key, "branches", NULL, &temp_error); if (configured_branches == NULL && temp_error != NULL) { if (g_error_matches (temp_error, G_KEY_FILE_ERROR, G_KEY_FILE_ERROR_KEY_NOT_FOUND)) { g_clear_error (&temp_error); fetch_all_refs = TRUE; } else { g_propagate_error (error, temp_error); goto out; } } else fetch_all_refs = FALSE; if (fetch_all_refs) { summary_uri = soup_uri_copy (pull_data->base_uri); path = g_build_filename (soup_uri_get_path (summary_uri), "refs", "summary", NULL); soup_uri_set_path (summary_uri, path); if (!fetch_uri_contents_utf8_sync (pull_data, summary_uri, &summary_data, cancellable, error)) goto out; if (!parse_ref_summary (summary_data, &requested_refs_to_fetch, error)) goto out; } else { char **branches_iter = configured_branches; if (!(branches_iter && *branches_iter)) g_print ("No configured branches for remote %s\n", pull_data->remote_name); for (;branches_iter && *branches_iter; branches_iter++) { const char *branch = *branches_iter; char *contents; if (!fetch_ref_contents (pull_data, branch, &contents, cancellable, error)) goto out; /* Transfer ownership of contents */ g_hash_table_insert (requested_refs_to_fetch, g_strdup (branch), contents); } } } if (!ostree_repo_prepare_transaction (pull_data->repo, &pull_data->transaction_resuming, cancellable, error)) goto out; pull_data->metadata_objects_to_fetch = ot_waitable_queue_new (); pull_data->metadata_objects_to_scan = ot_waitable_queue_new (); pull_data->metadata_thread = g_thread_new ("metadatascan", metadata_thread_main, pull_data); g_hash_table_iter_init (&hash_iter, commits_to_fetch); while (g_hash_table_iter_next (&hash_iter, &key, &value)) { const char *commit = value; ot_waitable_queue_push (pull_data->metadata_objects_to_scan, pull_worker_message_new (PULL_MSG_SCAN, ostree_object_name_serialize (commit, OSTREE_OBJECT_TYPE_COMMIT))); } g_hash_table_iter_init (&hash_iter, requested_refs_to_fetch); while (g_hash_table_iter_next (&hash_iter, &key, &value)) { const char *ref = key; const char *sha256 = value; ot_waitable_queue_push (pull_data->metadata_objects_to_scan, pull_worker_message_new (PULL_MSG_SCAN, ostree_object_name_serialize (sha256, OSTREE_OBJECT_TYPE_COMMIT))); g_hash_table_insert (updated_refs, g_strdup (ref), g_strdup (sha256)); } { queue_src = ot_waitable_queue_create_source (pull_data->metadata_objects_to_fetch); g_source_set_callback (queue_src, (GSourceFunc)on_metadata_objects_to_fetch_ready, pull_data, NULL); g_source_attach (queue_src, pull_data->main_context); g_source_unref (queue_src); } /* Prime the message queue */ pull_data->idle_serial++; ot_waitable_queue_push (pull_data->metadata_objects_to_scan, pull_worker_message_new (PULL_MSG_MAIN_IDLE, GUINT_TO_POINTER (pull_data->idle_serial))); /* Now await work completion */ if (!run_mainloop_monitor_fetcher (pull_data)) goto out; g_hash_table_iter_init (&hash_iter, updated_refs); while (g_hash_table_iter_next (&hash_iter, &key, &value)) { const char *ref = key; const char *checksum = value; gs_free char *remote_ref = NULL; gs_free char *original_rev = NULL; remote_ref = g_strdup_printf ("%s/%s", pull_data->remote_name, ref); if (!ostree_repo_resolve_rev (pull_data->repo, remote_ref, TRUE, &original_rev, error)) goto out; if (original_rev && strcmp (checksum, original_rev) == 0) { g_print ("remote %s is unchanged from %s\n", remote_ref, original_rev); } else { ostree_repo_transaction_set_ref (pull_data->repo, pull_data->remote_name, ref, checksum); g_print ("remote %s is now %s\n", remote_ref, checksum); } } if (!ostree_repo_commit_transaction (pull_data->repo, NULL, cancellable, error)) goto out; end_time = g_get_monotonic_time (); bytes_transferred = ostree_fetcher_bytes_transferred (pull_data->fetcher); if (bytes_transferred > 0) { guint shift; if (bytes_transferred < 1024) shift = 1; else shift = 1024; g_print ("%u metadata, %u content objects fetched; %" G_GUINT64_FORMAT " %s transferred in %u seconds\n", pull_data->n_fetched_metadata, pull_data->n_fetched_content, (guint64)(bytes_transferred / shift), shift == 1 ? "B" : "KiB", (guint) ((end_time - start_time) / G_USEC_PER_SEC)); } ret = TRUE; out: if (pull_data->main_context) g_main_context_unref (pull_data->main_context); if (pull_data->loop) g_main_loop_unref (pull_data->loop); g_strfreev (configured_branches); g_clear_object (&pull_data->fetcher); g_free (pull_data->remote_name); if (pull_data->base_uri) soup_uri_free (pull_data->base_uri); if (queue_src) g_source_destroy (queue_src); if (pull_data->metadata_thread) { ot_waitable_queue_push (pull_data->metadata_objects_to_scan, pull_worker_message_new (PULL_MSG_QUIT, NULL)); g_thread_join (pull_data->metadata_thread); } g_clear_pointer (&pull_data->metadata_objects_to_scan, (GDestroyNotify) ot_waitable_queue_unref); g_clear_pointer (&pull_data->metadata_objects_to_fetch, (GDestroyNotify) ot_waitable_queue_unref); g_clear_pointer (&pull_data->scanned_metadata, (GDestroyNotify) g_hash_table_unref); g_clear_pointer (&pull_data->requested_content, (GDestroyNotify) g_hash_table_unref); g_clear_pointer (&pull_data->requested_metadata, (GDestroyNotify) g_hash_table_unref); g_clear_pointer (&remote_config, (GDestroyNotify) g_key_file_unref); if (summary_uri) soup_uri_free (summary_uri); return ret; }
gboolean ostree_builtin_commit (int argc, char **argv, GCancellable *cancellable, GError **error) { GOptionContext *context; gs_unref_object OstreeRepo *repo = NULL; gboolean ret = FALSE; gboolean skip_commit = FALSE; gs_unref_object GFile *arg = NULL; gs_free char *parent = NULL; gs_free char *commit_checksum = NULL; gs_unref_object GFile *root = NULL; gs_unref_variant GVariant *metadata = NULL; gs_unref_variant GVariant *detached_metadata = NULL; gs_unref_object OstreeMutableTree *mtree = NULL; gs_free char *tree_type = NULL; gs_unref_hashtable GHashTable *mode_adds = NULL; OstreeRepoCommitModifierFlags flags = 0; OstreeRepoCommitModifier *modifier = NULL; OstreeRepoTransactionStats stats; context = g_option_context_new ("[PATH] - Commit a new revision"); if (!ostree_option_context_parse (context, options, &argc, &argv, OSTREE_BUILTIN_FLAG_NONE, &repo, cancellable, error)) goto out; if (opt_statoverride_file) { if (!parse_statoverride_file (&mode_adds, cancellable, error)) goto out; } if (opt_metadata_strings) { if (!parse_keyvalue_strings (opt_metadata_strings, &metadata, error)) goto out; } if (opt_detached_metadata_strings) { if (!parse_keyvalue_strings (opt_detached_metadata_strings, &detached_metadata, error)) goto out; } if (!opt_branch) { g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED, "A branch must be specified with --branch"); goto out; } if (opt_no_xattrs) flags |= OSTREE_REPO_COMMIT_MODIFIER_FLAGS_SKIP_XATTRS; if (opt_generate_sizes) flags |= OSTREE_REPO_COMMIT_MODIFIER_FLAGS_GENERATE_SIZES; if (opt_disable_fsync) ostree_repo_set_disable_fsync (repo, TRUE); if (flags != 0 || opt_owner_uid >= 0 || opt_owner_gid >= 0 || opt_statoverride_file != NULL || opt_no_xattrs) { modifier = ostree_repo_commit_modifier_new (flags, commit_filter, mode_adds, NULL); } if (!ostree_repo_resolve_rev (repo, opt_branch, TRUE, &parent, error)) goto out; if (!opt_subject && !opt_body) { if (!commit_editor (repo, opt_branch, &opt_subject, &opt_body, cancellable, error)) goto out; } if (!opt_subject) { g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED, "A subject must be specified with --subject"); goto out; } if (!ostree_repo_prepare_transaction (repo, NULL, cancellable, error)) goto out; if (opt_link_checkout_speedup && !ostree_repo_scan_hardlinks (repo, cancellable, error)) goto out; mtree = ostree_mutable_tree_new (); if (argc <= 1 && (opt_trees == NULL || opt_trees[0] == NULL)) { char *current_dir = g_get_current_dir (); arg = g_file_new_for_path (current_dir); g_free (current_dir); if (!ostree_repo_write_directory_to_mtree (repo, arg, mtree, modifier, cancellable, error)) goto out; } else if (opt_trees != NULL) { const char *const*tree_iter; const char *tree; const char *eq; for (tree_iter = (const char *const*)opt_trees; *tree_iter; tree_iter++) { tree = *tree_iter; eq = strchr (tree, '='); if (!eq) { g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "Missing type in tree specification '%s'", tree); goto out; } g_free (tree_type); tree_type = g_strndup (tree, eq - tree); tree = eq + 1; g_clear_object (&arg); if (strcmp (tree_type, "dir") == 0) { arg = g_file_new_for_path (tree); if (!ostree_repo_write_directory_to_mtree (repo, arg, mtree, modifier, cancellable, error)) goto out; } else if (strcmp (tree_type, "tar") == 0) { arg = g_file_new_for_path (tree); if (!ostree_repo_write_archive_to_mtree (repo, arg, mtree, modifier, opt_tar_autocreate_parents, cancellable, error)) goto out; } else if (strcmp (tree_type, "ref") == 0) { if (!ostree_repo_read_commit (repo, tree, &arg, NULL, cancellable, error)) goto out; if (!ostree_repo_write_directory_to_mtree (repo, arg, mtree, modifier, cancellable, error)) goto out; } else { g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "Invalid tree type specification '%s'", tree_type); goto out; } } } else { g_assert (argc > 1); arg = g_file_new_for_path (argv[1]); if (!ostree_repo_write_directory_to_mtree (repo, arg, mtree, modifier, cancellable, error)) goto out; } if (mode_adds && g_hash_table_size (mode_adds) > 0) { GHashTableIter hash_iter; gpointer key, value; g_hash_table_iter_init (&hash_iter, mode_adds); while (g_hash_table_iter_next (&hash_iter, &key, &value)) { g_printerr ("Unmatched statoverride path: %s\n", (char*)key); } g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "Unmatched statoverride paths"); goto out; } if (!ostree_repo_write_mtree (repo, mtree, &root, cancellable, error)) goto out; if (opt_skip_if_unchanged && parent) { gs_unref_object GFile *parent_root; if (!ostree_repo_read_commit (repo, parent, &parent_root, NULL, cancellable, error)) goto out; if (g_file_equal (root, parent_root)) skip_commit = TRUE; } if (!skip_commit) { if (!ostree_repo_write_commit (repo, parent, opt_subject, opt_body, metadata, OSTREE_REPO_FILE (root), &commit_checksum, cancellable, error)) goto out; if (detached_metadata) { if (!ostree_repo_write_commit_detached_metadata (repo, commit_checksum, detached_metadata, cancellable, error)) goto out; } #ifdef HAVE_GPGME if (opt_key_ids) { char **iter; for (iter = opt_key_ids; iter && *iter; iter++) { const char *keyid = *iter; if (!ostree_repo_sign_commit (repo, commit_checksum, keyid, opt_gpg_homedir, cancellable, error)) goto out; } } #endif ostree_repo_transaction_set_ref (repo, NULL, opt_branch, commit_checksum); if (!ostree_repo_commit_transaction (repo, &stats, cancellable, error)) goto out; } else { commit_checksum = g_strdup (parent); } if (opt_table_output) { g_print ("Commit: %s\n", commit_checksum); g_print ("Metadata Total: %u\n", stats.metadata_objects_total); g_print ("Metadata Written: %u\n", stats.metadata_objects_written); g_print ("Content Total: %u\n", stats.content_objects_total); g_print ("Content Written: %u\n", stats.content_objects_written); g_print ("Content Bytes Written: %" G_GUINT64_FORMAT "\n", stats.content_bytes_written); } else { g_print ("%s\n", commit_checksum); } ret = TRUE; out: if (repo) ostree_repo_abort_transaction (repo, cancellable, NULL); if (context) g_option_context_free (context); if (modifier) ostree_repo_commit_modifier_unref (modifier); return ret; }
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; }
gboolean flatpak_builtin_build_commit_from (int argc, char **argv, GCancellable *cancellable, GError **error) { g_autoptr(GOptionContext) context = NULL; g_autoptr(GFile) dst_repofile = NULL; g_autoptr(OstreeRepo) dst_repo = NULL; g_autoptr(GFile) src_repofile = NULL; g_autoptr(OstreeRepo) src_repo = NULL; g_autofree char *src_repo_uri = NULL; const char *dst_repo_arg; const char **dst_refs; int n_dst_refs = 0; g_autoptr(FlatpakRepoTransaction) transaction = NULL; g_autoptr(GPtrArray) src_refs = NULL; g_autoptr(GPtrArray) resolved_src_refs = NULL; OstreeRepoCommitState src_commit_state; struct timespec ts; guint64 timestamp; int i; context = g_option_context_new (_("DST-REPO [DST-REF…] - Make a new commit from existing commits")); g_option_context_set_translation_domain (context, GETTEXT_PACKAGE); if (!flatpak_option_context_parse (context, options, &argc, &argv, FLATPAK_BUILTIN_FLAG_NO_DIR, NULL, cancellable, error)) return FALSE; if (argc < 2) return usage_error (context, _("DST-REPO must be specified"), error); dst_repo_arg = argv[1]; dst_refs = (const char **) argv + 2; n_dst_refs = argc - 2; if (opt_src_repo == NULL && n_dst_refs != 1) return usage_error (context, _("If --src-repo is not specified, exactly one destination ref must be specified"), error); if (opt_src_ref != NULL && n_dst_refs != 1) return usage_error (context, _("If --src-ref is specified, exactly one destination ref must be specified"), error); if (opt_src_repo == NULL && opt_src_ref == NULL) return flatpak_fail (error, _("Either --src-repo or --src-ref must be specified.")); /* Always create a commit if we're eol:ing, even though the app is the same */ if (opt_endoflife != NULL || opt_endoflife_rebase != NULL) opt_force = TRUE; if (opt_endoflife_rebase) { opt_endoflife_rebase_new = g_new0 (char *, g_strv_length (opt_endoflife_rebase)); for (i = 0; opt_endoflife_rebase[i] != NULL; i++) { char *rebase_old = opt_endoflife_rebase[i]; char *rebase_new = strchr (rebase_old, '='); if (rebase_new == NULL) { return usage_error (context, _("Invalid argument format of use --end-of-life-rebase=OLDID=NEWID"), error); } *rebase_new = 0; rebase_new++; if (!flatpak_is_valid_name (rebase_old, error)) return glnx_prefix_error (error, _("Invalid name %s in --end-of-life-rebase"), rebase_old); if (!flatpak_is_valid_name (rebase_new, error)) return glnx_prefix_error (error, _("Invalid name %s in --end-of-life-rebase"), rebase_new); opt_endoflife_rebase_new[i] = rebase_new; } } if (opt_timestamp) { if (!parse_datetime (&ts, opt_timestamp, NULL)) return flatpak_fail (error, _("Could not parse '%s'"), opt_timestamp); } dst_repofile = g_file_new_for_commandline_arg (dst_repo_arg); if (!g_file_query_exists (dst_repofile, cancellable)) return flatpak_fail (error, _("'%s' is not a valid repository"), dst_repo_arg); dst_repo = ostree_repo_new (dst_repofile); if (!ostree_repo_open (dst_repo, cancellable, error)) return FALSE; if (opt_disable_fsync) ostree_repo_set_disable_fsync (dst_repo, TRUE); if (opt_src_repo) { src_repofile = g_file_new_for_commandline_arg (opt_src_repo); if (!g_file_query_exists (src_repofile, cancellable)) return flatpak_fail (error, _("'%s' is not a valid repository"), opt_src_repo); src_repo_uri = g_file_get_uri (src_repofile); src_repo = ostree_repo_new (src_repofile); if (!ostree_repo_open (src_repo, cancellable, error)) return FALSE; } else { src_repo = g_object_ref (dst_repo); } src_refs = g_ptr_array_new_with_free_func (g_free); if (opt_src_ref) { g_assert (n_dst_refs == 1); g_ptr_array_add (src_refs, g_strdup (opt_src_ref)); } else { g_assert (opt_src_repo != NULL); if (n_dst_refs == 0) { g_autofree const char **keys = NULL; g_autoptr(GHashTable) all_src_refs = NULL; if (!ostree_repo_list_refs (src_repo, NULL, &all_src_refs, cancellable, error)) return FALSE; keys = (const char **) g_hash_table_get_keys_as_array (all_src_refs, NULL); for (i = 0; keys[i] != NULL; i++) { if (g_str_has_prefix (keys[i], "runtime/") || g_str_has_prefix (keys[i], "app/")) g_ptr_array_add (src_refs, g_strdup (keys[i])); } n_dst_refs = src_refs->len; dst_refs = (const char **) src_refs->pdata; } else { for (i = 0; i < n_dst_refs; i++) g_ptr_array_add (src_refs, g_strdup (dst_refs[i])); } } resolved_src_refs = g_ptr_array_new_with_free_func (g_free); for (i = 0; i < src_refs->len; i++) { const char *src_ref = g_ptr_array_index (src_refs, i); char *resolved_ref; if (!ostree_repo_resolve_rev (src_repo, src_ref, FALSE, &resolved_ref, error)) return FALSE; g_ptr_array_add (resolved_src_refs, resolved_ref); } if (src_repo_uri != NULL) { OstreeRepoPullFlags pullflags = 0; GVariantBuilder builder; g_autoptr(OstreeAsyncProgress) progress = NULL; g_auto(GLnxConsoleRef) console = { 0, }; g_autoptr(GVariant) options = NULL; gboolean res; if (opt_untrusted) pullflags |= OSTREE_REPO_PULL_FLAGS_UNTRUSTED; glnx_console_lock (&console); if (console.is_tty) progress = ostree_async_progress_new_and_connect (ostree_repo_pull_default_console_progress_changed, &console); g_variant_builder_init (&builder, G_VARIANT_TYPE ("a{sv}")); g_variant_builder_add (&builder, "{s@v}", "flags", g_variant_new_variant (g_variant_new_int32 (pullflags))); g_variant_builder_add (&builder, "{s@v}", "refs", g_variant_new_variant (g_variant_new_strv ((const char * const *) resolved_src_refs->pdata, resolved_src_refs->len))); g_variant_builder_add (&builder, "{s@v}", "depth", g_variant_new_variant (g_variant_new_int32 (0))); options = g_variant_ref_sink (g_variant_builder_end (&builder)); res = ostree_repo_pull_with_options (dst_repo, src_repo_uri, options, progress, cancellable, error); if (progress) ostree_async_progress_finish (progress); if (!res) return FALSE; } /* By now we have the commit with commit_id==resolved_ref and dependencies in dst_repo. We now create a new * commit based on the toplevel tree ref from that commit. * This is equivalent to: * ostree commit --skip-if-unchanged --repo=${destrepo} --tree=ref=${resolved_ref} */ transaction = flatpak_repo_transaction_start (dst_repo, cancellable, error); if (transaction == NULL) return FALSE; for (i = 0; i < resolved_src_refs->len; i++) { const char *dst_ref = dst_refs[i]; const char *resolved_ref = g_ptr_array_index (resolved_src_refs, i); g_autofree char *dst_parent = NULL; g_autoptr(GFile) dst_parent_root = NULL; g_autoptr(GFile) src_ref_root = NULL; g_autoptr(GVariant) src_commitv = NULL; g_autoptr(GVariant) dst_commitv = NULL; g_autoptr(OstreeMutableTree) mtree = NULL; g_autoptr(GFile) dst_root = NULL; g_autoptr(GVariant) commitv_metadata = NULL; g_autoptr(GVariant) metadata = NULL; const char *subject; const char *body; g_autofree char *commit_checksum = NULL; GVariantBuilder metadata_builder; gint j; const char *dst_collection_id = NULL; const char *main_collection_id = NULL; g_autoptr(GPtrArray) collection_ids = NULL; if (!ostree_repo_resolve_rev (dst_repo, dst_ref, TRUE, &dst_parent, error)) return FALSE; if (dst_parent != NULL && !ostree_repo_read_commit (dst_repo, dst_parent, &dst_parent_root, NULL, cancellable, error)) return FALSE; if (!ostree_repo_read_commit (dst_repo, resolved_ref, &src_ref_root, NULL, cancellable, error)) return FALSE; if (!ostree_repo_load_commit (dst_repo, resolved_ref, &src_commitv, &src_commit_state, error)) return FALSE; if (src_commit_state & OSTREE_REPO_COMMIT_STATE_PARTIAL) return flatpak_fail (error, _("Can't commit from partial source commit.")); /* Don't create a new commit if this is the same tree */ if (!opt_force && dst_parent_root != NULL && g_file_equal (dst_parent_root, src_ref_root)) { g_print (_("%s: no change\n"), dst_ref); continue; } mtree = ostree_mutable_tree_new (); if (!ostree_repo_write_directory_to_mtree (dst_repo, src_ref_root, mtree, NULL, cancellable, error)) return FALSE; if (!ostree_repo_write_mtree (dst_repo, mtree, &dst_root, cancellable, error)) return FALSE; commitv_metadata = g_variant_get_child_value (src_commitv, 0); g_variant_get_child (src_commitv, 3, "&s", &subject); if (opt_subject) subject = (const char *) opt_subject; g_variant_get_child (src_commitv, 4, "&s", &body); if (opt_body) body = (const char *) opt_body; dst_collection_id = ostree_repo_get_collection_id (dst_repo); collection_ids = g_ptr_array_new_with_free_func (g_free); if (dst_collection_id) { main_collection_id = dst_collection_id; g_ptr_array_add (collection_ids, g_strdup (dst_collection_id)); } if (opt_extra_collection_ids != NULL) { for (j = 0; opt_extra_collection_ids[j] != NULL; j++) { const char *cid = opt_extra_collection_ids[j]; if (main_collection_id == NULL) main_collection_id = cid; /* Fall back to first arg */ if (g_strcmp0 (cid, dst_collection_id) != 0) g_ptr_array_add (collection_ids, g_strdup (cid)); } } g_ptr_array_sort (collection_ids, (GCompareFunc) flatpak_strcmp0_ptr); /* Copy old metadata */ g_variant_builder_init (&metadata_builder, G_VARIANT_TYPE ("a{sv}")); /* Bindings. xa.ref is deprecated but added anyway for backwards compatibility. */ g_variant_builder_add (&metadata_builder, "{sv}", "ostree.collection-binding", g_variant_new_string (main_collection_id ? main_collection_id : "")); if (collection_ids->len > 0) { g_autoptr(GVariantBuilder) cr_builder = g_variant_builder_new (G_VARIANT_TYPE ("a(ss)")); for (j = 0; j < collection_ids->len; j++) g_variant_builder_add (cr_builder, "(ss)", g_ptr_array_index (collection_ids, j), dst_ref); g_variant_builder_add (&metadata_builder, "{sv}", "ostree.collection-refs-binding", g_variant_builder_end (cr_builder)); } g_variant_builder_add (&metadata_builder, "{sv}", "ostree.ref-binding", g_variant_new_strv (&dst_ref, 1)); g_variant_builder_add (&metadata_builder, "{sv}", "xa.ref", g_variant_new_string (dst_ref)); /* Record the source commit. This is nice to have, but it also means the commit-from gets a different commit id, which avoids problems with e.g. sharing .commitmeta files (signatures) */ g_variant_builder_add (&metadata_builder, "{sv}", "xa.from_commit", g_variant_new_string (resolved_ref)); for (j = 0; j < g_variant_n_children (commitv_metadata); j++) { g_autoptr(GVariant) child = g_variant_get_child_value (commitv_metadata, j); g_autoptr(GVariant) keyv = g_variant_get_child_value (child, 0); const char *key = g_variant_get_string (keyv, NULL); if (strcmp (key, "xa.ref") == 0 || strcmp (key, "xa.from_commit") == 0 || strcmp (key, "ostree.collection-binding") == 0 || strcmp (key, "ostree.collection-refs-binding") == 0 || strcmp (key, "ostree.ref-binding") == 0) continue; if (opt_endoflife && strcmp (key, OSTREE_COMMIT_META_KEY_ENDOFLIFE) == 0) continue; if (opt_endoflife_rebase && strcmp (key, OSTREE_COMMIT_META_KEY_ENDOFLIFE_REBASE) == 0) continue; g_variant_builder_add_value (&metadata_builder, child); } if (opt_endoflife && *opt_endoflife) g_variant_builder_add (&metadata_builder, "{sv}", OSTREE_COMMIT_META_KEY_ENDOFLIFE, g_variant_new_string (opt_endoflife)); if (opt_endoflife_rebase) { g_auto(GStrv) dst_ref_parts = g_strsplit (dst_ref, "/", 0); for (j = 0; opt_endoflife_rebase[j] != NULL; j++) { const char *old_prefix = opt_endoflife_rebase[j]; if (flatpak_has_name_prefix (dst_ref_parts[1], old_prefix)) { g_autofree char *new_id = g_strconcat (opt_endoflife_rebase_new[j], dst_ref_parts[1] + strlen(old_prefix), NULL); g_autofree char *rebased_ref = g_build_filename (dst_ref_parts[0], new_id, dst_ref_parts[2], dst_ref_parts[3], NULL); g_variant_builder_add (&metadata_builder, "{sv}", OSTREE_COMMIT_META_KEY_ENDOFLIFE_REBASE, g_variant_new_string (rebased_ref)); break; } } } timestamp = ostree_commit_get_timestamp (src_commitv); if (opt_timestamp) timestamp = ts.tv_sec; metadata = g_variant_ref_sink (g_variant_builder_end (&metadata_builder)); if (!ostree_repo_write_commit_with_time (dst_repo, dst_parent, subject, body, metadata, OSTREE_REPO_FILE (dst_root), timestamp, &commit_checksum, cancellable, error)) return FALSE; g_print ("%s: %s\n", dst_ref, commit_checksum); if (!ostree_repo_load_commit (dst_repo, commit_checksum, &dst_commitv, NULL, error)) return FALSE; /* This doesn't copy the detached metadata. I'm not sure if this is a problem. * The main thing there is commit signatures, and we can't copy those, as the commit hash changes. */ if (opt_gpg_key_ids) { char **iter; for (iter = opt_gpg_key_ids; iter && *iter; iter++) { const char *keyid = *iter; g_autoptr(GError) my_error = NULL; if (!ostree_repo_sign_commit (dst_repo, commit_checksum, keyid, opt_gpg_homedir, cancellable, &my_error) && !g_error_matches (my_error, G_IO_ERROR, G_IO_ERROR_EXISTS)) { g_propagate_error (error, g_steal_pointer (&my_error)); return FALSE; } } } if (dst_collection_id != NULL) { OstreeCollectionRef ref = { (char *) dst_collection_id, (char *) dst_ref }; ostree_repo_transaction_set_collection_ref (dst_repo, &ref, commit_checksum); } else { ostree_repo_transaction_set_ref (dst_repo, NULL, dst_ref, commit_checksum); } if (opt_extra_collection_ids) { for (j = 0; opt_extra_collection_ids[j] != NULL; j++) { OstreeCollectionRef ref = { (char *) opt_extra_collection_ids[j], (char *) dst_ref }; ostree_repo_transaction_set_collection_ref (dst_repo, &ref, commit_checksum); } } /* Copy + Rewrite any deltas */ { const char *from[2]; gsize j, n_from = 0; if (dst_parent != NULL) from[n_from++] = dst_parent; from[n_from++] = NULL; for (j = 0; j < n_from; j++) { g_autoptr(GError) local_error = NULL; if (!rewrite_delta (src_repo, resolved_ref, dst_repo, commit_checksum, dst_commitv, from[j], &local_error)) g_debug ("Failed to copy delta: %s", local_error->message); } } } if (!ostree_repo_commit_transaction (dst_repo, NULL, cancellable, error)) return FALSE; if (opt_update_appstream && !flatpak_repo_generate_appstream (dst_repo, (const char **) opt_gpg_key_ids, opt_gpg_homedir, 0, cancellable, error)) return FALSE; if (!opt_no_update_summary && !flatpak_repo_update (dst_repo, (const char **) opt_gpg_key_ids, opt_gpg_homedir, cancellable, error)) return FALSE; return TRUE; }