static gboolean print_if_found (OstreeRepo *repo, OstreeObjectType objtype, const char *checksum, gboolean *inout_was_found, GCancellable *cancellable, GError **error) { gboolean ret = FALSE; gboolean have_object = FALSE; if (*inout_was_found) return TRUE; if (!ostree_repo_has_object (repo, objtype, checksum, &have_object, cancellable, error)) goto out; if (have_object) { if (!print_object (repo, objtype, checksum, error)) goto out; *inout_was_found = TRUE; } ret = TRUE; out: return ret; }
/** * ostree_repo_prune_static_deltas: * @self: Repo * @commit: (allow-none): ASCII SHA256 checksum for commit, or %NULL for each * non existing commit * @cancellable: Cancellable * @error: Error * * Prune static deltas, if COMMIT is specified then delete static delta files only * targeting that commit; otherwise any static delta of non existing commits are * deleted. * * Locking: exclusive */ gboolean ostree_repo_prune_static_deltas (OstreeRepo *self, const char *commit, GCancellable *cancellable, GError **error) { g_autoptr(OstreeRepoAutoLock) lock = _ostree_repo_auto_lock_push (self, OSTREE_REPO_LOCK_EXCLUSIVE, cancellable, error); if (!lock) return FALSE; g_autoptr(GPtrArray) deltas = NULL; if (!ostree_repo_list_static_delta_names (self, &deltas, cancellable, error)) return FALSE; for (guint i = 0; i < deltas->len; i++) { const char *deltaname = deltas->pdata[i]; const char *dash = strchr (deltaname, '-'); const char *to = NULL; g_autofree char *from = NULL; if (!dash) { to = deltaname; } else { from = g_strndup (deltaname, dash - deltaname); to = dash + 1; } if (commit) { if (g_strcmp0 (to, commit)) continue; } else { gboolean have_commit; if (!ostree_repo_has_object (self, OSTREE_OBJECT_TYPE_COMMIT, to, &have_commit, cancellable, error)) return FALSE; if (have_commit) continue; } g_debug ("Trying to prune static delta %s", deltaname); g_autofree char *deltadir = _ostree_get_relative_static_delta_path (from, to, NULL); if (!glnx_shutil_rm_rf_at (self->repo_dir_fd, deltadir, cancellable, error)) return FALSE; } return TRUE; }
gboolean _ostree_repo_static_delta_part_have_all_objects (OstreeRepo *repo, GVariant *checksum_array, gboolean *out_have_all, GCancellable *cancellable, GError **error) { gboolean ret = FALSE; guint8 *checksums_data; guint i,n_checksums; gboolean have_object = TRUE; if (!_ostree_static_delta_parse_checksum_array (checksum_array, &checksums_data, &n_checksums, error)) goto out; for (i = 0; i < n_checksums; i++) { guint8 objtype = *checksums_data; const guint8 *csum = checksums_data + 1; char tmp_checksum[OSTREE_SHA256_STRING_LEN+1]; if (G_UNLIKELY(!ostree_validate_structureof_objtype (objtype, error))) goto out; ostree_checksum_inplace_from_bytes (csum, tmp_checksum); if (!ostree_repo_has_object (repo, (OstreeObjectType) objtype, tmp_checksum, &have_object, cancellable, error)) goto out; if (!have_object) break; checksums_data += OSTREE_STATIC_DELTA_OBJTYPE_CSUM_LEN; } ret = TRUE; *out_have_all = have_object; out: return ret; }
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 scan_dirtree_object (OtPullData *pull_data, const char *checksum, int recursion_depth, GCancellable *cancellable, GError **error) { gboolean ret = FALSE; int i, n; gs_unref_variant GVariant *tree = NULL; gs_unref_variant GVariant *files_variant = NULL; gs_unref_variant GVariant *dirs_variant = NULL; if (recursion_depth > OSTREE_MAX_RECURSION) { g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "Exceeded maximum recursion"); goto out; } if (!ostree_repo_load_variant (pull_data->repo, OSTREE_OBJECT_TYPE_DIR_TREE, checksum, &tree, error)) goto out; /* PARSE OSTREE_SERIALIZED_TREE_VARIANT */ files_variant = g_variant_get_child_value (tree, 0); dirs_variant = g_variant_get_child_value (tree, 1); n = g_variant_n_children (files_variant); for (i = 0; i < n; i++) { const char *filename; gboolean file_is_stored; gs_unref_variant GVariant *csum = NULL; gs_free char *file_checksum = NULL; g_variant_get_child (files_variant, i, "(&s@ay)", &filename, &csum); if (!ot_util_filename_validate (filename, error)) goto out; file_checksum = ostree_checksum_from_bytes_v (csum); if (!ostree_repo_has_object (pull_data->repo, OSTREE_OBJECT_TYPE_FILE, file_checksum, &file_is_stored, cancellable, error)) goto out; if (!file_is_stored && !g_hash_table_lookup (pull_data->requested_content, file_checksum)) { g_hash_table_insert (pull_data->requested_content, file_checksum, file_checksum); ot_waitable_queue_push (pull_data->metadata_objects_to_fetch, pull_worker_message_new (PULL_MSG_FETCH, ostree_object_name_serialize (file_checksum, OSTREE_OBJECT_TYPE_FILE))); file_checksum = NULL; /* Transfer ownership to hash */ } } n = g_variant_n_children (dirs_variant); for (i = 0; i < n; i++) { const char *dirname; gs_unref_variant GVariant *tree_csum = NULL; gs_unref_variant GVariant *meta_csum = NULL; g_variant_get_child (dirs_variant, i, "(&s@ay@ay)", &dirname, &tree_csum, &meta_csum); if (!ot_util_filename_validate (dirname, error)) goto out; if (!scan_one_metadata_object (pull_data, ostree_checksum_bytes_peek (tree_csum), OSTREE_OBJECT_TYPE_DIR_TREE, recursion_depth + 1, cancellable, error)) goto out; if (!scan_one_metadata_object (pull_data, ostree_checksum_bytes_peek (meta_csum), OSTREE_OBJECT_TYPE_DIR_META, recursion_depth + 1, cancellable, error)) goto out; } ret = TRUE; out: return ret; }
/** * ostree_repo_static_delta_execute_offline: * @self: Repo * @dir_or_file: Path to a directory containing static delta data, or directly to the superblock * @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_or_file, gboolean skip_validation, GCancellable *cancellable, GError **error) { gboolean ret = FALSE; guint i, n; const char *dir_or_file_path = NULL; glnx_fd_close int meta_fd = -1; glnx_fd_close int dfd = -1; g_autoptr(GVariant) meta = NULL; g_autoptr(GVariant) headers = NULL; g_autoptr(GVariant) metadata = NULL; g_autoptr(GVariant) fallback = NULL; g_autofree char *to_checksum = NULL; g_autofree char *from_checksum = NULL; g_autofree char *basename = NULL; dir_or_file_path = gs_file_get_path_cached (dir_or_file); /* First, try opening it as a directory */ dfd = glnx_opendirat_with_errno (AT_FDCWD, dir_or_file_path, TRUE); if (dfd < 0) { if (errno != ENOTDIR) { glnx_set_error_from_errno (error); goto out; } else { g_autofree char *dir = dirname (g_strdup (dir_or_file_path)); basename = g_path_get_basename (dir_or_file_path); if (!glnx_opendirat (AT_FDCWD, dir, TRUE, &dfd, error)) goto out; } } else basename = g_strdup ("superblock"); meta_fd = openat (dfd, basename, O_RDONLY | O_CLOEXEC); if (meta_fd < 0) { glnx_set_error_from_errno (error); goto out; } if (!ot_util_variant_map_fd (meta_fd, 0, G_VARIANT_TYPE (OSTREE_STATIC_DELTA_SUPERBLOCK_FORMAT), FALSE, &meta, error)) goto out; /* Parsing OSTREE_STATIC_DELTA_SUPERBLOCK_FORMAT */ metadata = g_variant_get_child_value (meta, 0); /* Write the to-commit object */ { g_autoptr(GVariant) to_csum_v = NULL; g_autoptr(GVariant) from_csum_v = NULL; g_autoptr(GVariant) to_commit = NULL; gboolean have_to_commit; gboolean have_from_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); from_csum_v = g_variant_get_child_value (meta, 2); if (g_variant_n_children (from_csum_v) > 0) { if (!ostree_validate_structureof_csum_v (from_csum_v, error)) goto out; from_checksum = ostree_checksum_from_bytes_v (from_csum_v); if (!ostree_repo_has_object (self, OSTREE_OBJECT_TYPE_COMMIT, from_checksum, &have_from_commit, cancellable, error)) goto out; if (!have_from_commit) { g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "Commit %s, which is the delta source, is not in repository", from_checksum); goto out; } } if (!ostree_repo_has_object (self, OSTREE_OBJECT_TYPE_COMMIT, to_checksum, &have_to_commit, cancellable, error)) goto out; if (!have_to_commit) { g_autofree char *detached_path = _ostree_get_relative_static_delta_path (from_checksum, to_checksum, "commitmeta"); g_autoptr(GVariant) detached_data = NULL; detached_data = g_variant_lookup_value (metadata, detached_path, G_VARIANT_TYPE("a{sv}")); if (detached_data && !ostree_repo_write_commit_detached_metadata (self, to_checksum, detached_data, cancellable, error)) goto out; 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++) { guint32 version; guint64 size; guint64 usize; const guchar *csum; char checksum[OSTREE_SHA256_STRING_LEN+1]; gboolean have_all; g_autoptr(GInputStream) part_in = NULL; g_autoptr(GVariant) inline_part_data = NULL; g_autoptr(GVariant) header = NULL; g_autoptr(GVariant) csum_v = NULL; g_autoptr(GVariant) objects = NULL; g_autoptr(GVariant) part = NULL; g_autofree char *deltapart_path = NULL; OstreeStaticDeltaOpenFlags delta_open_flags = skip_validation ? OSTREE_STATIC_DELTA_OPEN_FLAGS_SKIP_CHECKSUM : 0; header = g_variant_get_child_value (headers, i); g_variant_get (header, "(u@aytt@ay)", &version, &csum_v, &size, &usize, &objects); if (version > OSTREE_DELTAPART_VERSION) { g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "Delta part has too new version %u", version); goto out; } 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; ostree_checksum_inplace_from_bytes (csum, checksum); deltapart_path = _ostree_get_relative_static_delta_part_path (from_checksum, to_checksum, i); inline_part_data = g_variant_lookup_value (metadata, deltapart_path, G_VARIANT_TYPE("(yay)")); if (inline_part_data) { g_autoptr(GBytes) inline_part_bytes = g_variant_get_data_as_bytes (inline_part_data); part_in = g_memory_input_stream_new_from_bytes (inline_part_bytes); /* For inline parts, we don't checksum, because it's * included with the metadata, so we're not trying to * protect against MITM or such. Non-security related * checksums should be done at the underlying storage layer. */ delta_open_flags |= OSTREE_STATIC_DELTA_OPEN_FLAGS_SKIP_CHECKSUM; if (!_ostree_static_delta_part_open (part_in, inline_part_bytes, delta_open_flags, NULL, &part, cancellable, error)) goto out; } else { g_autofree char *relpath = g_strdup_printf ("%u", i); /* TODO avoid malloc here */ glnx_fd_close int part_fd = openat (dfd, relpath, O_RDONLY | O_CLOEXEC); if (part_fd < 0) { glnx_set_error_from_errno (error); g_prefix_error (error, "Opening deltapart '%s': ", deltapart_path); goto out; } part_in = g_unix_input_stream_new (part_fd, FALSE); if (!_ostree_static_delta_part_open (part_in, NULL, delta_open_flags, checksum, &part, cancellable, error)) goto out; } if (!_ostree_static_delta_part_execute (self, objects, part, skip_validation, NULL, cancellable, error)) { g_prefix_error (error, "Executing delta part %i: ", i); goto out; } } ret = TRUE; out: return ret; }
/** * 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; }