static gboolean write_checksum_file_at (OstreeRepo *self, int dfd, const char *name, const char *sha256, GCancellable *cancellable, GError **error) { gboolean ret = FALSE; const char *lastslash; if (!ostree_validate_checksum_string (sha256, error)) goto out; if (ostree_validate_checksum_string (name, NULL)) { g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "Rev name '%s' looks like a checksum", name); goto out; } if (!*name) { g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "Invalid empty ref name"); goto out; } lastslash = strrchr (name, '/'); if (lastslash) { char *parent = strdupa (name); parent[lastslash - name] = '\0'; if (!glnx_shutil_mkdir_p_at (dfd, parent, 0777, cancellable, error)) goto out; } { size_t l = strlen (sha256); char *bufnl = alloca (l + 2); memcpy (bufnl, sha256, l); bufnl[l] = '\n'; bufnl[l+1] = '\0'; if (!_ostree_repo_file_replace_contents (self, dfd, name, (guint8*)bufnl, l + 1, cancellable, error)) goto out; } ret = TRUE; out: return ret; }
static gboolean fetch_ref_contents (OtPullData *pull_data, const char *ref, char **out_contents, GCancellable *cancellable, GError **error) { gboolean ret = FALSE; gs_free char *ret_contents = NULL; SoupURI *target_uri = NULL; target_uri = suburi_new (pull_data->base_uri, "refs", "heads", ref, NULL); if (!fetch_uri_contents_utf8_sync (pull_data, target_uri, &ret_contents, cancellable, error)) goto out; g_strchomp (ret_contents); if (!ostree_validate_checksum_string (ret_contents, error)) goto out; ret = TRUE; ot_transfer_out_value (out_contents, &ret_contents); out: if (target_uri) soup_uri_free (target_uri); return ret; }
static gboolean parse_bootdir_name (const char *name, char **out_osname, char **out_csum) { const char *lastdash; if (out_osname) *out_osname = NULL; if (out_csum) *out_csum = NULL; lastdash = strrchr (name, '-'); if (!lastdash) return FALSE; if (!ostree_validate_checksum_string (lastdash + 1, NULL)) return FALSE; if (out_osname) *out_osname = g_strndup (name, lastdash - name); if (out_csum) *out_csum = g_strdup (lastdash + 1); return TRUE; }
static gboolean parse_rev_file (OstreeRepo *self, GFile *f, char **sha256, GError **error) { gboolean ret = FALSE; GError *temp_error = NULL; gs_free char *rev = NULL; if ((rev = gs_file_load_contents_utf8 (f, NULL, &temp_error)) == NULL) goto out; if (rev == NULL) { if (g_error_matches (temp_error, G_FILE_ERROR, G_FILE_ERROR_NOENT)) { g_clear_error (&temp_error); } else { g_propagate_error (error, temp_error); goto out; } } else { g_strchomp (rev); } if (g_str_has_prefix (rev, "ref: ")) { gs_unref_object GFile *ref = NULL; char *ref_sha256; gboolean subret; ref = g_file_resolve_relative_path (self->local_heads_dir, rev + 5); subret = parse_rev_file (self, ref, &ref_sha256, error); if (!subret) { g_free (ref_sha256); goto out; } g_free (rev); rev = ref_sha256; } else { if (!ostree_validate_checksum_string (rev, error)) goto out; } ot_transfer_out_value(sha256, &rev); ret = TRUE; out: return ret; }
/** * rpmostreed_parse_revision: * @revision: Revision string * @out_checksum: (out) (allow-none): Commit checksum, or %NULL * @out_version: (out) (allow-none): Version value, or %NULL * @error: Error * * Determines @revision to either be a SHA256 checksum or a version metadata * value, sets one of @out_checksum or @out_version appropriately, and then * returns %TRUE. * * The @revision string may have a "revision=" prefix to denote a SHA256 * checksum, or a "version=" prefix to denote a version metadata value. If * the @revision string lacks either prefix, the function attempts to infer * the type of revision. The prefixes are case-insensitive. * * The only possible error is if a "revision=" prefix is given, but the * rest of the string is not a valid SHA256 checksum. In that case the * function sets @error and returns %FALSE. * * Returns: %TRUE on success, %FALSE of failure */ gboolean rpmostreed_parse_revision (const char *revision, char **out_checksum, char **out_version, GError **error) { const char *checksum = NULL; const char *version = NULL; gboolean ret = FALSE; g_return_val_if_fail (revision != NULL, FALSE); if (g_ascii_strncasecmp (revision, "revision=", 9) == 0) { checksum = revision + 9; /* Since this claims to be a checksum, fail if it isn't. */ if (!ostree_validate_checksum_string (checksum, error)) goto out; } else if (g_ascii_strncasecmp (revision, "version=", 8) == 0) { version = revision + 8; } else if (ostree_validate_checksum_string (revision, NULL)) { /* If it looks like a checksum, assume it is. */ checksum = revision; } else { /* Treat anything else as a version metadata value. */ version = revision; } if (out_checksum != NULL) *out_checksum = g_strdup (checksum); if (out_version != NULL) *out_version = g_strdup (version); ret = TRUE; out: return ret; }
static gboolean parse_ref_summary (const char *contents, GHashTable **out_refs, GError **error) { gboolean ret = FALSE; gs_unref_hashtable GHashTable *ret_refs = NULL; char **lines = NULL; char **iter = NULL; char *ref = NULL; char *sha256 = NULL; ret_refs = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free); lines = g_strsplit_set (contents, "\n", -1); for (iter = lines; *iter; iter++) { const char *line = *iter; const char *spc; if (!*line) continue; spc = strchr (line, ' '); if (!spc) { g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "Invalid ref summary file; missing ' ' in line"); goto out; } g_free (ref); ref = g_strdup (spc + 1); if (!ostree_validate_rev (ref, error)) goto out; g_free (sha256); sha256 = g_strndup (line, spc - line); if (!ostree_validate_checksum_string (sha256, error)) goto out; g_hash_table_replace (ret_refs, ref, sha256); /* Transfer ownership */ ref = NULL; sha256 = NULL; } ret = TRUE; ot_transfer_out_value (out_refs, &ret_refs); out: g_strfreev (lines); 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; }
/** * 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 resolve_refspec (OstreeRepo *self, const char *remote, const char *ref, gboolean allow_noent, char **out_rev, GError **error) { gboolean ret = FALSE; __attribute__((unused)) GCancellable *cancellable = NULL; GError *temp_error = NULL; g_autofree char *ret_rev = NULL; g_autoptr(GFile) child = NULL; g_return_val_if_fail (ref != NULL, FALSE); /* We intentionally don't allow a ref that looks like a checksum */ if (ostree_validate_checksum_string (ref, NULL)) { ret_rev = g_strdup (ref); } else if (remote != NULL) { child = ot_gfile_resolve_path_printf (self->remote_heads_dir, "%s/%s", remote, ref); if (!g_file_query_exists (child, NULL)) g_clear_object (&child); } else { child = g_file_resolve_relative_path (self->local_heads_dir, ref); if (!g_file_query_exists (child, NULL)) { g_clear_object (&child); child = g_file_resolve_relative_path (self->remote_heads_dir, ref); if (!g_file_query_exists (child, NULL)) { g_clear_object (&child); if (!find_ref_in_remotes (self, ref, &child, error)) goto out; } } } if (child) { if ((ret_rev = gs_file_load_contents_utf8 (child, NULL, &temp_error)) == NULL) { g_propagate_error (error, temp_error); g_prefix_error (error, "Couldn't open ref '%s': ", gs_file_get_path_cached (child)); goto out; } g_strchomp (ret_rev); if (!ostree_validate_checksum_string (ret_rev, error)) goto out; } else { if (!resolve_refspec_fallback (self, remote, ref, allow_noent, &ret_rev, cancellable, error)) goto out; } ot_transfer_out_value (out_rev, &ret_rev); ret = TRUE; out: return ret; }
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; g_autoptr(GHashTable) skip_list = NULL; OstreeRepoCommitModifierFlags flags = 0; OstreeRepoCommitModifier *modifier = NULL; OstreeRepoTransactionStats stats; struct CommitFilterData filter_data = { 0, }; 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) { mode_adds = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL); if (!parse_file_by_line (opt_statoverride_file, handle_statoverride_line, mode_adds, cancellable, error)) goto out; } if (opt_skiplist_file) { skip_list = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL); if (!parse_file_by_line (opt_skiplist_file, handle_skiplist_line, skip_list, 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 || opt_orphan)) { g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED, "A branch must be specified with --branch, or use --orphan"); 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_skiplist_file != NULL || opt_no_xattrs) { filter_data.mode_adds = mode_adds; filter_data.skip_list = skip_list; modifier = ostree_repo_commit_modifier_new (flags, commit_filter, &filter_data, NULL); } if (opt_parent) { if (g_str_equal (opt_parent, "none")) parent = NULL; else { if (!ostree_validate_checksum_string (opt_parent, error)) goto out; parent = g_strdup (opt_parent); } } else if (!opt_orphan) { if (!ostree_repo_resolve_rev (repo, opt_branch, TRUE, &parent, error)) { if (g_error_matches (*error, G_IO_ERROR, G_IO_ERROR_IS_DIRECTORY)) { /* A folder exists with the specified ref name, * which is handled by _ostree_repo_write_ref */ g_clear_error (error); } else goto out; } } if (opt_editor) { if (!commit_editor (repo, opt_branch, &opt_subject, &opt_body, cancellable, error)) 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)) { if (!ostree_repo_write_dfd_to_mtree (repo, AT_FDCWD, ".", 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) { if (!ostree_repo_write_dfd_to_mtree (repo, AT_FDCWD, tree, 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); if (!ostree_repo_write_dfd_to_mtree (repo, AT_FDCWD, argv[1], 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 (skip_list && g_hash_table_size (skip_list) > 0) { GHashTableIter hash_iter; gpointer key; g_hash_table_iter_init (&hash_iter, skip_list); while (g_hash_table_iter_next (&hash_iter, &key, NULL)) { g_printerr ("Unmatched skip-list path: %s\n", (char*)key); } g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "Unmatched skip-list 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; guint64 timestamp; if (!opt_timestamp) { GDateTime *now = g_date_time_new_now_utc (); timestamp = g_date_time_to_unix (now); g_date_time_unref (now); if (!ostree_repo_write_commit (repo, parent, opt_subject, opt_body, metadata, OSTREE_REPO_FILE (root), &commit_checksum, cancellable, error)) goto out; } else { struct timespec ts; if (!parse_datetime (&ts, opt_timestamp, NULL)) { g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "Could not parse '%s'", opt_timestamp); goto out; } timestamp = ts.tv_sec; if (!ostree_repo_write_commit_with_time (repo, parent, opt_subject, opt_body, metadata, OSTREE_REPO_FILE (root), timestamp, &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; } } if (opt_branch) ostree_repo_transaction_set_ref (repo, NULL, opt_branch, commit_checksum); else g_assert (opt_orphan); 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; }
static gboolean write_checksum_file (GFile *parentdir, const char *name, const char *sha256, GCancellable *cancellable, GError **error) { gboolean ret = FALSE; gsize bytes_written; int i; gs_unref_object GFile *parent = NULL; gs_unref_object GFile *child = NULL; gs_unref_object GOutputStream *out = NULL; gs_unref_ptrarray GPtrArray *components = NULL; if (!ostree_validate_checksum_string (sha256, error)) goto out; if (ostree_validate_checksum_string (name, NULL)) { g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "Rev name '%s' looks like a checksum", name); goto out; } if (!ot_util_path_split_validate (name, &components, error)) goto out; if (components->len == 0) { g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "Invalid empty ref name"); goto out; } parent = g_object_ref (parentdir); for (i = 0; i+1 < components->len; i++) { child = g_file_get_child (parent, (char*)components->pdata[i]); if (!gs_file_ensure_directory (child, FALSE, cancellable, error)) goto out; g_clear_object (&parent); parent = child; child = NULL; } child = g_file_get_child (parent, components->pdata[components->len - 1]); if ((out = (GOutputStream*)g_file_replace (child, NULL, FALSE, 0, cancellable, error)) == NULL) goto out; if (!g_output_stream_write_all (out, sha256, strlen (sha256), &bytes_written, cancellable, error)) goto out; if (!g_output_stream_write_all (out, "\n", 1, &bytes_written, cancellable, error)) goto out; if (!g_output_stream_close (out, cancellable, error)) goto out; ret = TRUE; out: return ret; }
static gboolean write_checksum_file_at (OstreeRepo *self, int dfd, const char *name, const char *sha256, GCancellable *cancellable, GError **error) { if (!ostree_validate_checksum_string (sha256, error)) return FALSE; if (ostree_validate_checksum_string (name, NULL)) return glnx_throw (error, "Rev name '%s' looks like a checksum", name); if (!*name) return glnx_throw (error, "Invalid empty ref name"); const char *lastslash = strrchr (name, '/'); if (lastslash) { char *parent = strdupa (name); parent[lastslash - name] = '\0'; if (!glnx_shutil_mkdir_p_at (dfd, parent, 0777, cancellable, error)) return FALSE; } { size_t l = strlen (sha256); char *bufnl = alloca (l + 2); g_autoptr(GError) temp_error = NULL; memcpy (bufnl, sha256, l); bufnl[l] = '\n'; bufnl[l+1] = '\0'; if (!_ostree_repo_file_replace_contents (self, dfd, name, (guint8*)bufnl, l + 1, cancellable, &temp_error)) { if (g_error_matches (temp_error, G_IO_ERROR, G_IO_ERROR_IS_DIRECTORY)) { g_autoptr(GHashTable) refs = NULL; GHashTableIter hashiter; gpointer hashkey, hashvalue; g_clear_error (&temp_error); if (!ostree_repo_list_refs (self, name, &refs, cancellable, error)) return FALSE; g_hash_table_iter_init (&hashiter, refs); while ((g_hash_table_iter_next (&hashiter, &hashkey, &hashvalue))) { if (strcmp (name, (char *)hashkey) != 0) return glnx_throw (error, "Conflict: %s exists under %s when attempting write", (char*)hashkey, name); } if (!glnx_shutil_rm_rf_at (dfd, name, cancellable, error)) return FALSE; if (!_ostree_repo_file_replace_contents (self, dfd, name, (guint8*)bufnl, l + 1, cancellable, error)) return FALSE; } else { g_propagate_error (error, g_steal_pointer (&temp_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; }
static gboolean resolve_refspec (OstreeRepo *self, const char *remote, const char *ref, gboolean allow_noent, gboolean fallback_remote, char **out_rev, GError **error) { __attribute__((unused)) GCancellable *cancellable = NULL; g_autofree char *ret_rev = NULL; glnx_fd_close int target_fd = -1; g_return_val_if_fail (ref != NULL, FALSE); /* We intentionally don't allow a ref that looks like a checksum */ if (ostree_validate_checksum_string (ref, NULL)) { ret_rev = g_strdup (ref); } else if (remote != NULL) { const char *remote_ref = glnx_strjoina ("refs/remotes/", remote, "/", ref); if (!ot_openat_ignore_enoent (self->repo_dir_fd, remote_ref, &target_fd, error)) return FALSE; } else { const char *local_ref = glnx_strjoina ("refs/heads/", ref); if (!ot_openat_ignore_enoent (self->repo_dir_fd, local_ref, &target_fd, error)) return FALSE; if (target_fd == -1 && fallback_remote) { local_ref = glnx_strjoina ("refs/remotes/", ref); if (!ot_openat_ignore_enoent (self->repo_dir_fd, local_ref, &target_fd, error)) return FALSE; if (target_fd == -1) { if (!find_ref_in_remotes (self, ref, &target_fd, error)) return FALSE; } } } if (target_fd != -1) { ret_rev = glnx_fd_readall_utf8 (target_fd, NULL, NULL, error); if (!ret_rev) { g_prefix_error (error, "Couldn't open ref '%s': ", ref); return FALSE; } g_strchomp (ret_rev); if (!ostree_validate_checksum_string (ret_rev, error)) return FALSE; } else { if (!resolve_refspec_fallback (self, remote, ref, allow_noent, fallback_remote, &ret_rev, cancellable, error)) return FALSE; } ot_transfer_out_value (out_rev, &ret_rev); return TRUE; }
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; }