diff --git a/src/libostree/ostree-repo-static-delta-core.c b/src/libostree/ostree-repo-static-delta-core.c index e3432aa5..c58e3683 100644 --- a/src/libostree/ostree-repo-static-delta-core.c +++ b/src/libostree/ostree-repo-static-delta-core.c @@ -1205,3 +1205,168 @@ ostree_repo_static_delta_verify_signature (OstreeRepo *self, return _ostree_repo_static_delta_verify_signature (self, delta_fd, sign, out_success_message, error); } + +static void +null_or_ptr_array_unref (GPtrArray *array) +{ + if (array != NULL) + g_ptr_array_unref (array); +} + +static gboolean +file_has_content (OstreeRepo *repo, + const char *subpath, + GBytes *data, + GCancellable *cancellable) +{ + struct stat stbuf; + glnx_autofd int existing_fd = -1; + + if (!glnx_fstatat (repo->repo_dir_fd, subpath, &stbuf, 0, NULL)) + return FALSE; + + if (stbuf.st_size != g_bytes_get_size (data)) + return FALSE; + + if (!glnx_openat_rdonly (repo->repo_dir_fd, subpath, TRUE, &existing_fd, NULL)) + return FALSE; + + g_autoptr(GBytes) existing_data = glnx_fd_readall_bytes (existing_fd, cancellable, NULL); + if (existing_data == NULL) + return FALSE; + + return g_bytes_equal (existing_data, data); +} + +gboolean +_ostree_repo_static_delta_reindex (OstreeRepo *repo, + const char *opt_to_commit, + GCancellable *cancellable, + GError **error) +{ + g_autoptr(GPtrArray) all_deltas = NULL; + g_autoptr(GHashTable) deltas_to_commit_ht = NULL; /* map: to checksum -> ptrarray of from checksums (or NULL) */ + + deltas_to_commit_ht = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, (GDestroyNotify)null_or_ptr_array_unref); + + if (opt_to_commit == NULL) + { + g_autoptr(GPtrArray) old_indexes = NULL; + + /* To ensure all old index files either is regenerated, or + * removed, we initialize all existing indexes to NULL in the + * hashtable. */ + if (!ostree_repo_list_static_delta_indexes (repo, &old_indexes, cancellable, error)) + return FALSE; + + for (int i = 0; i < old_indexes->len; i++) + { + const char *old_index = g_ptr_array_index (old_indexes, i); + g_hash_table_insert (deltas_to_commit_ht, g_strdup (old_index), NULL); + } + } + else + { + if (!ostree_validate_checksum_string (opt_to_commit, error)) + return FALSE; + + /* We ensure the specific old index either is regenerated, or removed */ + g_hash_table_insert (deltas_to_commit_ht, g_strdup (opt_to_commit), NULL); + } + + if (!ostree_repo_list_static_delta_names (repo, &all_deltas, cancellable, error)) + return FALSE; + + for (int i = 0; i < all_deltas->len; i++) + { + const char *delta_name = g_ptr_array_index (all_deltas, i); + g_autofree char *from = NULL; + g_autofree char *to = NULL; + GPtrArray *deltas_to_commit = NULL; + + if (!_ostree_parse_delta_name (delta_name, &from, &to, error)) + return FALSE; + + if (opt_to_commit != NULL && strcmp (to, opt_to_commit) != 0) + continue; + + deltas_to_commit = g_hash_table_lookup (deltas_to_commit_ht, to); + if (deltas_to_commit == NULL) + { + deltas_to_commit = g_ptr_array_new_with_free_func (g_free); + g_hash_table_insert (deltas_to_commit_ht, g_steal_pointer (&to), deltas_to_commit); + } + + g_ptr_array_add (deltas_to_commit, g_steal_pointer (&from)); + } + + GLNX_HASH_TABLE_FOREACH_KV (deltas_to_commit_ht, const char*, to, GPtrArray*, froms) + { + g_autofree char *index_path = _ostree_get_relative_static_delta_index_path (to); + + if (froms == NULL) + { + /* No index to this checksum seen, delete if it exists */ + + g_debug ("Removing delta index for %s", to); + if (!ot_ensure_unlinked_at (repo->repo_dir_fd, index_path, error)) + return FALSE; + } + else + { + g_auto(GVariantDict) index_builder = OT_VARIANT_BUILDER_INITIALIZER; + g_auto(GVariantDict) deltas_builder = OT_VARIANT_BUILDER_INITIALIZER; + g_autoptr(GVariant) index_variant = NULL; + g_autoptr(GBytes) index = NULL; + + /* We sort on from here so that the index file is reproducible */ + g_ptr_array_sort (froms, (GCompareFunc)g_strcmp0); + + g_variant_dict_init (&deltas_builder, NULL); + + for (int i = 0; i < froms->len; i++) + { + const char *from = g_ptr_array_index (froms, i); + g_autofree char *delta_name = NULL; + GVariant *digest; + + digest = _ostree_repo_static_delta_superblock_digest (repo, from, to, cancellable, error); + if (digest == NULL) + return FALSE; + + if (from != NULL) + delta_name = g_strconcat (from, "-", to, NULL); + else + delta_name = g_strdup (to); + + g_variant_dict_insert_value (&deltas_builder, delta_name, digest); + } + + /* The toplevel of the index is an a{sv} for extensibility, and we use same key name (and format) as when + * storing deltas in the summary. */ + g_variant_dict_init (&index_builder, NULL); + + g_variant_dict_insert_value (&index_builder, OSTREE_SUMMARY_STATIC_DELTAS, g_variant_dict_end (&deltas_builder)); + + index_variant = g_variant_ref_sink (g_variant_dict_end (&index_builder)); + index = g_variant_get_data_as_bytes (index_variant); + + g_autofree char *index_dirname = g_path_get_dirname (index_path); + if (!glnx_shutil_mkdir_p_at (repo->repo_dir_fd, index_dirname, DEFAULT_DIRECTORY_MODE, cancellable, error)) + return FALSE; + + /* delta indexes are generally small and static, so reading it back and comparing is cheap, and it will + lower the write load (and particular sync-load) on the disk during reindexing (i.e. summary updates), */ + if (file_has_content (repo, index_path, index, cancellable)) + continue; + + g_debug ("Updating delta index for %s", to); + if (!glnx_file_replace_contents_at (repo->repo_dir_fd, index_path, + g_bytes_get_data (index, NULL), g_bytes_get_size (index), + 0, cancellable, error)) + return FALSE; + } + } + + return TRUE; +} diff --git a/src/libostree/ostree-repo-static-delta-private.h b/src/libostree/ostree-repo-static-delta-private.h index 5a2e6879..d6c706da 100644 --- a/src/libostree/ostree-repo-static-delta-private.h +++ b/src/libostree/ostree-repo-static-delta-private.h @@ -227,6 +227,11 @@ _ostree_repo_static_delta_delete (OstreeRepo *repo, const char *delta_id, GCancellable *cancellable, GError **error); +gboolean +_ostree_repo_static_delta_reindex (OstreeRepo *repo, + const char *opt_to_commit, + GCancellable *cancellable, + GError **error); /* Used for static deltas which due to a historical mistake are * inconsistent endian. diff --git a/src/libostree/ostree-repo.c b/src/libostree/ostree-repo.c index ba3e877f..3a331c90 100644 --- a/src/libostree/ostree-repo.c +++ b/src/libostree/ostree-repo.c @@ -5927,6 +5927,9 @@ ostree_repo_regenerate_summary (OstreeRepo *self, g_variant_ref_sink (summary); } + if (!_ostree_repo_static_delta_reindex (self, NULL, cancellable, error)) + return FALSE; + if (!_ostree_repo_file_replace_contents (self, self->repo_dir_fd, "summary",