static gboolean maybe_prune_loose_object (OtPruneData *data, OstreeRepoPruneFlags flags, const char *checksum, OstreeObjectType objtype, GCancellable *cancellable, GError **error) { g_autoptr(GVariant) key = NULL; key = ostree_object_name_serialize (checksum, objtype); if (!g_hash_table_lookup_extended (data->reachable, key, NULL, NULL)) { g_debug ("Pruning unneeded object %s.%s", checksum, ostree_object_type_to_string (objtype)); if (!(flags & OSTREE_REPO_PRUNE_FLAGS_NO_PRUNE)) { guint64 storage_size = 0; if (objtype == OSTREE_OBJECT_TYPE_COMMIT) { if (!prune_commitpartial_file (data->repo, checksum, cancellable, error)) return FALSE; } if (!ostree_repo_query_object_storage_size (data->repo, objtype, checksum, &storage_size, cancellable, error)) return FALSE; if (!ostree_repo_delete_object (data->repo, objtype, checksum, cancellable, error)) return FALSE; data->freed_bytes += storage_size; } if (OSTREE_OBJECT_TYPE_IS_META (objtype)) data->n_unreachable_meta++; else data->n_unreachable_content++; } else { g_debug ("Keeping needed object %s.%s", checksum, ostree_object_type_to_string (objtype)); if (OSTREE_OBJECT_TYPE_IS_META (objtype)) data->n_reachable_meta++; else data->n_reachable_content++; } return TRUE; }
gboolean ostree_checksum_file_from_input (GFileInfo *file_info, GVariant *xattrs, GInputStream *in, OstreeObjectType objtype, guchar **out_csum, GCancellable *cancellable, GError **error) { gboolean ret = FALSE; ot_lfree guchar *ret_csum = NULL; GChecksum *checksum = NULL; checksum = g_checksum_new (G_CHECKSUM_SHA256); if (OSTREE_OBJECT_TYPE_IS_META (objtype)) { if (!ot_gio_splice_update_checksum (NULL, in, checksum, cancellable, error)) goto out; } else if (g_file_info_get_file_type (file_info) == G_FILE_TYPE_DIRECTORY) { ot_lvariant GVariant *dirmeta = ostree_create_directory_metadata (file_info, xattrs); g_checksum_update (checksum, g_variant_get_data (dirmeta), g_variant_get_size (dirmeta)); } else { ot_lvariant GVariant *file_header = NULL; file_header = ostree_file_header_new (file_info, xattrs); if (!ostree_write_file_header_update_checksum (NULL, file_header, checksum, cancellable, error)) goto out; if (g_file_info_get_file_type (file_info) == G_FILE_TYPE_REGULAR) { if (!ot_gio_splice_update_checksum (NULL, in, checksum, cancellable, error)) goto out; } } ret_csum = ot_csum_from_gchecksum (checksum); ret = TRUE; ot_transfer_out_value (out_csum, &ret_csum); out: g_clear_pointer (&checksum, (GDestroyNotify)g_checksum_free); return ret; }
static void enqueue_one_object_request (OtPullData *pull_data, GVariant *object_name, gboolean is_detached_meta) { const char *checksum; OstreeObjectType objtype; SoupURI *obj_uri = NULL; gboolean is_meta; FetchObjectData *fetch_data; gs_free char *objpath = NULL; ostree_object_name_deserialize (object_name, &checksum, &objtype); if (is_detached_meta) { char buf[_OSTREE_LOOSE_PATH_MAX]; _ostree_loose_path_with_suffix (buf, checksum, OSTREE_OBJECT_TYPE_COMMIT, pull_data->remote_mode, "meta"); obj_uri = suburi_new (pull_data->base_uri, "objects", buf, NULL); } else { objpath = _ostree_get_relative_object_path (checksum, objtype, TRUE); obj_uri = suburi_new (pull_data->base_uri, objpath, NULL); } is_meta = OSTREE_OBJECT_TYPE_IS_META (objtype); if (is_meta) { pull_data->n_outstanding_metadata_fetches++; pull_data->n_requested_metadata++; } else { pull_data->n_outstanding_content_fetches++; pull_data->n_requested_content++; } fetch_data = g_new0 (FetchObjectData, 1); fetch_data->pull_data = pull_data; fetch_data->object = g_variant_ref (object_name); fetch_data->is_detached_meta = is_detached_meta; ostree_fetcher_request_uri_with_partial_async (pull_data->fetcher, obj_uri, pull_data->cancellable, is_meta ? meta_fetch_on_complete : content_fetch_on_complete, fetch_data); soup_uri_free (obj_uri); }
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); }
char * ostree_get_relative_object_path (const char *checksum, OstreeObjectType type, gboolean compressed) { GString *path; g_assert (strlen (checksum) == 64); path = g_string_new ("objects/"); g_string_append_len (path, checksum, 2); g_string_append_c (path, '/'); g_string_append (path, checksum + 2); g_string_append_c (path, '.'); g_string_append (path, ostree_object_type_to_string (type)); if (!OSTREE_OBJECT_TYPE_IS_META (type) && compressed) g_string_append (path, "z"); return g_string_free (path, FALSE); }
static gboolean dispatch_open_splice_and_close (OstreeRepo *repo, StaticDeltaExecutionState *state, GCancellable *cancellable, GError **error) { gboolean ret = FALSE; if (!open_output_target (state, cancellable, error)) goto out; if (OSTREE_OBJECT_TYPE_IS_META (state->output_objtype)) { g_autoptr(GVariant) metadata = NULL; guint64 offset; guint64 length; if (!read_varuint64 (state, &length, error)) goto out; if (!read_varuint64 (state, &offset, error)) goto out; if (!validate_ofs (state, offset, length, error)) goto out; if (state->stats_only) { ret = TRUE; goto out; } metadata = g_variant_new_from_data (ostree_metadata_variant_type (state->output_objtype), state->payload_data + offset, length, TRUE, NULL, NULL); { g_autofree guchar *actual_csum = NULL; if (!ostree_repo_write_metadata (state->repo, state->output_objtype, state->checksum, metadata, &actual_csum, cancellable, error)) goto out; } } else { guint64 content_offset; guint64 objlen; g_autoptr(GInputStream) object_input = NULL; g_autoptr(GInputStream) memin = NULL; if (!do_content_open_generic (repo, state, cancellable, error)) goto out; if (!read_varuint64 (state, &state->content_size, error)) goto out; if (!read_varuint64 (state, &content_offset, error)) goto out; if (!validate_ofs (state, content_offset, state->content_size, error)) goto out; if (state->stats_only) { ret = TRUE; goto out; } /* Fast path for regular files to bare repositories */ if (S_ISREG (state->mode) && (repo->mode == OSTREE_REPO_MODE_BARE || repo->mode == OSTREE_REPO_MODE_BARE_USER)) { if (!_ostree_repo_open_content_bare (repo, state->checksum, state->content_size, &state->barecommitstate, &state->content_out, &state->have_obj, cancellable, error)) goto out; if (!state->have_obj) { if (!handle_untrusted_content_checksum (repo, state, cancellable, error)) goto out; if (!content_out_write (repo, state, state->payload_data + content_offset, state->content_size, cancellable, error)) goto out; } } else { /* Slower path, for symlinks and unpacking deltas into archive-z2 */ g_autoptr(GFileInfo) finfo = NULL; finfo = _ostree_header_gfile_info_new (state->mode, state->uid, state->gid); if (S_ISLNK (state->mode)) { g_autofree char *nulterminated_target = g_strndup ((char*)state->payload_data + content_offset, state->content_size); g_file_info_set_symlink_target (finfo, nulterminated_target); } else { g_assert (S_ISREG (state->mode)); g_file_info_set_size (finfo, state->content_size); memin = g_memory_input_stream_new_from_data (state->payload_data + content_offset, state->content_size, NULL); } if (!ostree_raw_file_to_content_stream (memin, finfo, state->xattrs, &object_input, &objlen, cancellable, error)) goto out; { g_autofree guchar *actual_csum = NULL; if (!ostree_repo_write_content (state->repo, state->checksum, object_input, objlen, &actual_csum, cancellable, error)) goto out; } } } if (!dispatch_close (repo, state, cancellable, error)) goto out; ret = TRUE; out: if (state->stats_only) (void) dispatch_close (repo, state, cancellable, NULL); if (!ret) g_prefix_error (error, "opcode open-splice-and-close: "); 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; }
static gboolean maybe_prune_loose_object (OtPruneData *data, OstreeRepoPruneFlags flags, const char *checksum, OstreeObjectType objtype, GCancellable *cancellable, GError **error) { gboolean reachable = FALSE; g_autoptr(GVariant) key = NULL; key = ostree_object_name_serialize (checksum, objtype); if (g_hash_table_lookup_extended (data->reachable, key, NULL, NULL)) reachable = TRUE; else { guint64 storage_size = 0; g_debug ("Pruning unneeded object %s.%s", checksum, ostree_object_type_to_string (objtype)); if (!ostree_repo_query_object_storage_size (data->repo, objtype, checksum, &storage_size, cancellable, error)) return FALSE; data->freed_bytes += storage_size; if (!(flags & OSTREE_REPO_PRUNE_FLAGS_NO_PRUNE)) { if (objtype == OSTREE_OBJECT_TYPE_PAYLOAD_LINK) { ssize_t size; char loose_path_buf[_OSTREE_LOOSE_PATH_MAX]; char target_checksum[OSTREE_SHA256_STRING_LEN+1]; char target_buf[_OSTREE_LOOSE_PATH_MAX + _OSTREE_PAYLOAD_LINK_PREFIX_LEN]; _ostree_loose_path (loose_path_buf, checksum, OSTREE_OBJECT_TYPE_PAYLOAD_LINK, data->repo->mode); size = readlinkat (data->repo->objects_dir_fd, loose_path_buf, target_buf, sizeof (target_buf)); if (size < 0) return glnx_throw_errno_prefix (error, "readlinkat"); if (size < OSTREE_SHA256_STRING_LEN + _OSTREE_PAYLOAD_LINK_PREFIX_LEN) return glnx_throw (error, "invalid data size for %s", loose_path_buf); sprintf (target_checksum, "%.2s%.62s", target_buf + _OSTREE_PAYLOAD_LINK_PREFIX_LEN, target_buf + _OSTREE_PAYLOAD_LINK_PREFIX_LEN + 3); g_autoptr(GVariant) target_key = ostree_object_name_serialize (target_checksum, OSTREE_OBJECT_TYPE_FILE); if (g_hash_table_lookup_extended (data->reachable, target_key, NULL, NULL)) { guint64 target_storage_size = 0; if (!ostree_repo_query_object_storage_size (data->repo, OSTREE_OBJECT_TYPE_FILE, target_checksum, &target_storage_size, cancellable, error)) return FALSE; reachable = target_storage_size >= data->repo->payload_link_threshold; if (reachable) goto exit; } } else if (objtype == OSTREE_OBJECT_TYPE_COMMIT) { if (!ostree_repo_mark_commit_partial (data->repo, checksum, FALSE, error)) return FALSE; } if (!ostree_repo_delete_object (data->repo, objtype, checksum, cancellable, error)) return FALSE; } if (OSTREE_OBJECT_TYPE_IS_META (objtype)) data->n_unreachable_meta++; else data->n_unreachable_content++; } exit: if (reachable) { g_debug ("Keeping needed object %s.%s", checksum, ostree_object_type_to_string (objtype)); if (OSTREE_OBJECT_TYPE_IS_META (objtype)) data->n_reachable_meta++; else data->n_reachable_content++; } return TRUE; }