static gboolean dispatch_set_read_source (OstreeRepo *repo, StaticDeltaExecutionState *state, GCancellable *cancellable, GError **error) { gboolean ret = FALSE; guint64 source_offset; if (state->read_source_fd) { (void) close (state->read_source_fd); state->read_source_fd = -1; } if (!read_varuint64 (state, &source_offset, error)) goto out; if (!validate_ofs (state, source_offset, 32, error)) goto out; g_free (state->read_source_object); state->read_source_object = ostree_checksum_from_bytes (state->payload_data + source_offset); if (!_ostree_repo_read_bare_fd (repo, state->read_source_object, &state->read_source_fd, cancellable, error)) goto out; ret = TRUE; out: if (!ret) g_prefix_error (error, "opcode set-read-source: "); return ret; }
gboolean _ostree_static_delta_part_validate (OstreeRepo *repo, GInputStream *in, guint part_offset, const char *expected_checksum, GCancellable *cancellable, GError **error) { gboolean ret = FALSE; g_autofree guchar *actual_checksum_bytes = NULL; g_autofree char *actual_checksum = NULL; if (!ot_gio_checksum_stream (in, &actual_checksum_bytes, cancellable, error)) goto out; actual_checksum = ostree_checksum_from_bytes (actual_checksum_bytes); if (strcmp (actual_checksum, expected_checksum) != 0) { g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "Checksum mismatch in static delta part %u; expected=%s actual=%s", part_offset, expected_checksum, actual_checksum); goto out; } ret = TRUE; out: return ret; }
gboolean add_file_to_mtree (GFile *file, const char *name, OstreeRepo *repo, OstreeMutableTree *mtree, GCancellable *cancellable, GError **error) { g_autoptr(GFileInfo) file_info = NULL; g_autoptr(GInputStream) raw_input = NULL; g_autoptr(GInputStream) input = NULL; guint64 length; g_autofree guchar *child_file_csum = NULL; g_autofree char *tmp_checksum = NULL; file_info = g_file_query_info (file, "standard::size", 0, cancellable, error); if (file_info == NULL) return FALSE; g_file_info_set_name (file_info, name); g_file_info_set_file_type (file_info, G_FILE_TYPE_REGULAR); g_file_info_set_attribute_uint32 (file_info, "unix::uid", 0); g_file_info_set_attribute_uint32 (file_info, "unix::gid", 0); g_file_info_set_attribute_uint32 (file_info, "unix::mode", 0100644); raw_input = (GInputStream *) g_file_read (file, cancellable, error); if (raw_input == NULL) return FALSE; if (!ostree_raw_file_to_content_stream (raw_input, file_info, NULL, &input, &length, cancellable, error)) return FALSE; if (!ostree_repo_write_content (repo, NULL, input, length, &child_file_csum, cancellable, error)) return FALSE; tmp_checksum = ostree_checksum_from_bytes (child_file_csum); if (!ostree_mutable_tree_replace_file (mtree, name, tmp_checksum, error)) return FALSE; return TRUE; }
static void on_metadata_writed (GObject *object, GAsyncResult *result, gpointer user_data) { FetchObjectData *fetch_data = user_data; OtPullData *pull_data = fetch_data->pull_data; GError *local_error = NULL; GError **error = &local_error; const char *expected_checksum; OstreeObjectType objtype; gs_free char *checksum = NULL; gs_free guchar *csum = NULL; if (!ostree_repo_write_metadata_finish ((OstreeRepo*)object, result, &csum, error)) goto out; checksum = ostree_checksum_from_bytes (csum); ostree_object_name_deserialize (fetch_data->object, &expected_checksum, &objtype); g_assert (OSTREE_OBJECT_TYPE_IS_META (objtype)); g_debug ("write of %s complete", ostree_object_to_string (checksum, objtype)); if (strcmp (checksum, expected_checksum) != 0) { g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "Corrupted metadata object; checksum expected='%s' actual='%s'", expected_checksum, checksum); goto out; } pull_data->metadata_scan_idle = FALSE; ot_waitable_queue_push (pull_data->metadata_objects_to_scan, pull_worker_message_new (PULL_MSG_SCAN, g_variant_ref (fetch_data->object))); out: pull_data->n_outstanding_metadata_write_requests--; (void) gs_file_unlink (fetch_data->temp_path, NULL, NULL); g_object_unref (fetch_data->temp_path); g_variant_unref (fetch_data->object); g_free (fetch_data); check_outstanding_requests_handle_error (pull_data, local_error); }
static void on_checksum_received (GObject *obj, GAsyncResult *result, gpointer user_data) { g_autofree guchar *csum = NULL; g_autofree char *checksum = NULL; AsyncChecksumData *data = user_data; if (ostree_checksum_file_async_finish ((GFile*)obj, result, &csum, data->error)) { checksum = ostree_checksum_from_bytes (csum); g_print ("%s\n", checksum); } g_main_loop_quit (data->loop); }
static gboolean get_file_checksum (OstreeDiffFlags flags, GFile *f, GFileInfo *f_info, char **out_checksum, GCancellable *cancellable, GError **error) { g_autofree char *ret_checksum = NULL; if (OSTREE_IS_REPO_FILE (f)) { ret_checksum = g_strdup (ostree_repo_file_get_checksum ((OstreeRepoFile*)f)); } else { g_autoptr(GVariant) xattrs = NULL; g_autoptr(GInputStream) in = NULL; if (!(flags & OSTREE_DIFF_FLAGS_IGNORE_XATTRS)) { if (!glnx_dfd_name_get_all_xattrs (AT_FDCWD, gs_file_get_path_cached (f), &xattrs, cancellable, error)) return FALSE; } if (g_file_info_get_file_type (f_info) == G_FILE_TYPE_REGULAR) { in = (GInputStream*)g_file_read (f, cancellable, error); if (!in) return FALSE; } g_autofree guchar *csum = NULL; if (!ostree_checksum_file_from_input (f_info, xattrs, in, OSTREE_OBJECT_TYPE_FILE, &csum, cancellable, error)) return FALSE; ret_checksum = ostree_checksum_from_bytes (csum); } ot_transfer_out_value(out_checksum, &ret_checksum); return TRUE; }
static void content_fetch_on_write_complete (GObject *object, GAsyncResult *result, gpointer user_data) { FetchObjectData *fetch_data = user_data; OtPullData *pull_data = fetch_data->pull_data; GError *local_error = NULL; GError **error = &local_error; OstreeObjectType objtype; const char *expected_checksum; gs_free guchar *csum = NULL; gs_free char *checksum = NULL; if (!ostree_repo_write_content_finish ((OstreeRepo*)object, result, &csum, error)) goto out; checksum = ostree_checksum_from_bytes (csum); ostree_object_name_deserialize (fetch_data->object, &expected_checksum, &objtype); g_assert (objtype == OSTREE_OBJECT_TYPE_FILE); g_debug ("write of %s complete", ostree_object_to_string (checksum, objtype)); if (strcmp (checksum, expected_checksum) != 0) { g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "Corrupted content object; checksum expected='%s' actual='%s'", expected_checksum, checksum); goto out; } pull_data->n_fetched_content++; out: pull_data->n_outstanding_content_write_requests--; check_outstanding_requests_handle_error (pull_data, local_error); (void) gs_file_unlink (fetch_data->temp_path, NULL, NULL); g_object_unref (fetch_data->temp_path); g_variant_unref (fetch_data->object); g_free (fetch_data); }
static gboolean scan_one_metadata_object (OtPullData *pull_data, const guchar *csum, OstreeObjectType objtype, guint recursion_depth, GCancellable *cancellable, GError **error) { gboolean ret = FALSE; gs_unref_variant GVariant *object = NULL; gs_free char *tmp_checksum = NULL; gboolean is_requested; gboolean is_stored; tmp_checksum = ostree_checksum_from_bytes (csum); object = ostree_object_name_serialize (tmp_checksum, objtype); if (g_hash_table_lookup (pull_data->scanned_metadata, object)) return TRUE; is_requested = g_hash_table_lookup (pull_data->requested_metadata, tmp_checksum) != NULL; if (!ostree_repo_has_object (pull_data->repo, objtype, tmp_checksum, &is_stored, cancellable, error)) goto out; if (!is_stored && !is_requested) { char *duped_checksum = g_strdup (tmp_checksum); g_hash_table_insert (pull_data->requested_metadata, duped_checksum, duped_checksum); if (objtype == OSTREE_OBJECT_TYPE_COMMIT) ot_waitable_queue_push (pull_data->metadata_objects_to_fetch, pull_worker_message_new (PULL_MSG_FETCH_DETACHED_METADATA, g_variant_ref (object))); else ot_waitable_queue_push (pull_data->metadata_objects_to_fetch, pull_worker_message_new (PULL_MSG_FETCH, g_variant_ref (object))); } else if (is_stored) { if (pull_data->transaction_resuming || is_requested) { switch (objtype) { case OSTREE_OBJECT_TYPE_COMMIT: if (!scan_commit_object (pull_data, tmp_checksum, recursion_depth, pull_data->cancellable, error)) goto out; break; case OSTREE_OBJECT_TYPE_DIR_META: break; case OSTREE_OBJECT_TYPE_DIR_TREE: if (!scan_dirtree_object (pull_data, tmp_checksum, recursion_depth, pull_data->cancellable, error)) goto out; break; case OSTREE_OBJECT_TYPE_FILE: g_assert_not_reached (); break; } } g_hash_table_insert (pull_data->scanned_metadata, g_variant_ref (object), object); g_atomic_int_inc (&pull_data->n_scanned_metadata); } ret = TRUE; out: return ret; }
static gboolean fsck_reachable_objects_from_commits (OtFsckData *data, GHashTable *commits, GCancellable *cancellable, GError **error) { gboolean ret = FALSE; GHashTableIter hash_iter; gpointer key, value; ot_lhash GHashTable *reachable_objects = NULL; ot_lobj GInputStream *input = NULL; ot_lobj GFileInfo *file_info = NULL; ot_lvariant GVariant *xattrs = NULL; ot_lvariant GVariant *metadata = NULL; ot_lfree guchar *computed_csum = NULL; ot_lfree char *tmp_checksum = NULL; reachable_objects = ostree_traverse_new_reachable (); g_hash_table_iter_init (&hash_iter, commits); while (g_hash_table_iter_next (&hash_iter, &key, &value)) { GVariant *serialized_key = key; const char *checksum; OstreeObjectType objtype; ostree_object_name_deserialize (serialized_key, &checksum, &objtype); g_assert (objtype == OSTREE_OBJECT_TYPE_COMMIT); if (!ostree_traverse_commit (data->repo, checksum, 0, reachable_objects, cancellable, error)) goto out; } g_hash_table_iter_init (&hash_iter, reachable_objects); while (g_hash_table_iter_next (&hash_iter, &key, &value)) { GVariant *serialized_key = key; const char *checksum; OstreeObjectType objtype; ostree_object_name_deserialize (serialized_key, &checksum, &objtype); g_clear_object (&input); g_clear_object (&file_info); g_clear_pointer (&xattrs, (GDestroyNotify) g_variant_unref); if (objtype == OSTREE_OBJECT_TYPE_COMMIT || objtype == OSTREE_OBJECT_TYPE_DIR_TREE || objtype == OSTREE_OBJECT_TYPE_DIR_META) { g_clear_pointer (&metadata, (GDestroyNotify) g_variant_unref); if (!ostree_repo_load_variant (data->repo, objtype, checksum, &metadata, error)) { g_prefix_error (error, "Loading metadata object %s: ", checksum); goto out; } if (objtype == OSTREE_OBJECT_TYPE_COMMIT) { if (!ostree_validate_structureof_commit (metadata, error)) { g_prefix_error (error, "While validating commit metadata '%s': ", checksum); goto out; } } else if (objtype == OSTREE_OBJECT_TYPE_DIR_TREE) { if (!ostree_validate_structureof_dirtree (metadata, error)) { g_prefix_error (error, "While validating directory tree '%s': ", checksum); goto out; } } else if (objtype == OSTREE_OBJECT_TYPE_DIR_META) { if (!ostree_validate_structureof_dirmeta (metadata, error)) { g_prefix_error (error, "While validating directory metadata '%s': ", checksum); goto out; } } else g_assert_not_reached (); input = g_memory_input_stream_new_from_data (g_variant_get_data (metadata), g_variant_get_size (metadata), NULL); } else if (objtype == OSTREE_OBJECT_TYPE_FILE) { guint32 mode; if (!ostree_repo_load_file (data->repo, checksum, &input, &file_info, &xattrs, cancellable, error)) { g_prefix_error (error, "Loading file object %s: ", checksum); goto out; } mode = g_file_info_get_attribute_uint32 (file_info, "unix::mode"); if (!ostree_validate_structureof_file_mode (mode, error)) { g_prefix_error (error, "While validating file '%s': ", checksum); goto out; } } else { g_assert_not_reached (); } g_free (computed_csum); if (!ostree_checksum_file_from_input (file_info, xattrs, input, objtype, &computed_csum, cancellable, error)) goto out; g_free (tmp_checksum); tmp_checksum = ostree_checksum_from_bytes (computed_csum); if (strcmp (checksum, tmp_checksum) != 0) { g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "corrupted object %s.%s; actual checksum: %s", checksum, ostree_object_type_to_string (objtype), tmp_checksum); goto out; } } ret = TRUE; out: return ret; }
static gboolean load_and_fsck_one_object (OstreeRepo *repo, const char *checksum, OstreeObjectType objtype, gboolean *out_found_corruption, GCancellable *cancellable, GError **error) { gboolean ret = FALSE; gboolean missing = FALSE; gs_unref_variant GVariant *metadata = NULL; gs_unref_object GInputStream *input = NULL; gs_unref_object GFileInfo *file_info = NULL; gs_unref_variant GVariant *xattrs = NULL; GError *temp_error = NULL; if (OSTREE_OBJECT_TYPE_IS_META (objtype)) { if (!ostree_repo_load_variant (repo, objtype, checksum, &metadata, &temp_error)) { if (g_error_matches (temp_error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND)) { g_clear_error (&temp_error); g_printerr ("Object missing: %s.%s\n", checksum, ostree_object_type_to_string (objtype)); missing = TRUE; } else { g_prefix_error (error, "Loading metadata object %s: ", checksum); goto out; } } else { if (objtype == OSTREE_OBJECT_TYPE_COMMIT) { if (!ostree_validate_structureof_commit (metadata, error)) { g_prefix_error (error, "While validating commit metadata '%s': ", checksum); goto out; } } else if (objtype == OSTREE_OBJECT_TYPE_DIR_TREE) { if (!ostree_validate_structureof_dirtree (metadata, error)) { g_prefix_error (error, "While validating directory tree '%s': ", checksum); goto out; } } else if (objtype == OSTREE_OBJECT_TYPE_DIR_META) { if (!ostree_validate_structureof_dirmeta (metadata, error)) { g_prefix_error (error, "While validating directory metadata '%s': ", checksum); goto out; } } input = g_memory_input_stream_new_from_data (g_variant_get_data (metadata), g_variant_get_size (metadata), NULL); } } else { guint32 mode; g_assert (objtype == OSTREE_OBJECT_TYPE_FILE); if (!ostree_repo_load_file (repo, checksum, &input, &file_info, &xattrs, cancellable, &temp_error)) { if (g_error_matches (temp_error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND)) { g_clear_error (&temp_error); g_printerr ("Object missing: %s.%s\n", checksum, ostree_object_type_to_string (objtype)); missing = TRUE; } else { g_prefix_error (error, "Loading file object %s: ", checksum); goto out; } } else { mode = g_file_info_get_attribute_uint32 (file_info, "unix::mode"); if (!ostree_validate_structureof_file_mode (mode, error)) { g_prefix_error (error, "While validating file '%s': ", checksum); goto out; } } } if (missing) { *out_found_corruption = TRUE; } else { gs_free guchar *computed_csum = NULL; gs_free char *tmp_checksum = NULL; if (!ostree_checksum_file_from_input (file_info, xattrs, input, objtype, &computed_csum, cancellable, error)) goto out; tmp_checksum = ostree_checksum_from_bytes (computed_csum); if (strcmp (checksum, tmp_checksum) != 0) { gs_free char *msg = g_strdup_printf ("corrupted object %s.%s; actual checksum: %s", checksum, ostree_object_type_to_string (objtype), tmp_checksum); if (opt_delete) { g_printerr ("%s\n", msg); (void) ostree_repo_delete_object (repo, objtype, checksum, cancellable, NULL); *out_found_corruption = TRUE; } else { g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED, msg); goto out; } } } ret = TRUE; out: return ret; }
char * ostree_checksum_from_bytes_v (GVariant *csum_bytes) { return ostree_checksum_from_bytes (ostree_checksum_bytes_peek (csum_bytes)); }
/** * ostree_repo_static_delta_execute_offline: * @self: Repo * @dir: Path to a directory containing static delta data * @skip_validation: If %TRUE, assume data integrity * @cancellable: Cancellable * @error: Error * * Given a directory representing an already-downloaded static delta * on disk, apply it, generating a new commit. The directory must be * named with the form "FROM-TO", where both are checksums, and it * must contain a file named "superblock", along with at least one part. */ gboolean ostree_repo_static_delta_execute_offline (OstreeRepo *self, GFile *dir, gboolean skip_validation, GCancellable *cancellable, GError **error) { gboolean ret = FALSE; guint i, n; gs_unref_object GFile *meta_file = g_file_get_child (dir, "superblock"); gs_unref_variant GVariant *meta = NULL; gs_unref_variant GVariant *headers = NULL; gs_unref_variant GVariant *fallback = NULL; if (!ot_util_variant_map (meta_file, G_VARIANT_TYPE (OSTREE_STATIC_DELTA_SUPERBLOCK_FORMAT), FALSE, &meta, error)) goto out; /* Parsing OSTREE_STATIC_DELTA_SUPERBLOCK_FORMAT */ /* Write the to-commit object */ { gs_unref_variant GVariant *to_csum_v = NULL; gs_free char *to_checksum = NULL; gs_unref_variant GVariant *to_commit = NULL; gboolean have_to_commit; to_csum_v = g_variant_get_child_value (meta, 3); if (!ostree_validate_structureof_csum_v (to_csum_v, error)) goto out; to_checksum = ostree_checksum_from_bytes_v (to_csum_v); if (!ostree_repo_has_object (self, OSTREE_OBJECT_TYPE_COMMIT, to_checksum, &have_to_commit, cancellable, error)) goto out; if (!have_to_commit) { to_commit = g_variant_get_child_value (meta, 4); if (!ostree_repo_write_metadata (self, OSTREE_OBJECT_TYPE_COMMIT, to_checksum, to_commit, NULL, cancellable, error)) goto out; } } fallback = g_variant_get_child_value (meta, 7); if (g_variant_n_children (fallback) > 0) { g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "Cannot execute delta offline: contains nonempty http fallback entries"); goto out; } headers = g_variant_get_child_value (meta, 6); n = g_variant_n_children (headers); for (i = 0; i < n; i++) { guint64 size; guint64 usize; const guchar *csum; gboolean have_all; gs_unref_variant GVariant *header = NULL; gs_unref_variant GVariant *csum_v = NULL; gs_unref_variant GVariant *objects = NULL; gs_unref_object GFile *part_path = NULL; gs_unref_object GInputStream *raw_in = NULL; gs_unref_object GInputStream *in = NULL; header = g_variant_get_child_value (headers, i); g_variant_get (header, "(@aytt@ay)", &csum_v, &size, &usize, &objects); if (!_ostree_repo_static_delta_part_have_all_objects (self, objects, &have_all, cancellable, error)) goto out; /* If we already have these objects, don't bother executing the * static delta. */ if (have_all) continue; csum = ostree_checksum_bytes_peek_validate (csum_v, error); if (!csum) goto out; part_path = ot_gfile_resolve_path_printf (dir, "%u", i); in = (GInputStream*)g_file_read (part_path, cancellable, error); if (!in) goto out; if (!skip_validation) { gs_free char *expected_checksum = ostree_checksum_from_bytes (csum); if (!_ostree_static_delta_part_validate (self, part_path, i, expected_checksum, cancellable, error)) goto out; } { GMappedFile *mfile = gs_file_map_noatime (part_path, cancellable, error); gs_unref_bytes GBytes *bytes = NULL; if (!mfile) goto out; bytes = g_mapped_file_get_bytes (mfile); g_mapped_file_unref (mfile); if (!_ostree_static_delta_part_execute (self, objects, bytes, cancellable, error)) { g_prefix_error (error, "executing delta part %i: ", i); goto out; } } } ret = TRUE; out: return ret; }