From eff00053efad68cafe53f1fcc6e67699a814c385 Mon Sep 17 00:00:00 2001 From: Jonathan Lebon Date: Mon, 8 May 2017 17:10:49 -0400 Subject: [PATCH] unpacker: embed RPM checksum in metadata In addition to the header checksum, we also want a checksum of the RPM file itself. This will allow us to know right away whether an RPM with the same NEVRA in the repos is actually the same one we already imported. Closes: #769 Approved by: cgwalters --- src/libpriv/rpmostree-core.c | 9 +++------ src/libpriv/rpmostree-rpm-util.c | 27 +++++++++++++++++++++++++++ src/libpriv/rpmostree-rpm-util.h | 5 +++++ src/libpriv/rpmostree-unpacker.c | 27 +++++++++++++++++++-------- 4 files changed, 54 insertions(+), 14 deletions(-) diff --git a/src/libpriv/rpmostree-core.c b/src/libpriv/rpmostree-core.c index df768887..3121045e 100644 --- a/src/libpriv/rpmostree-core.c +++ b/src/libpriv/rpmostree-core.c @@ -1360,12 +1360,9 @@ rpmostree_dnf_add_checksum_goal (GChecksum *checksum, for (i = 0; i < pkglist->len; i++) { DnfPackage *pkg = pkglist->pdata[i]; - int pkg_checksum_type; - const unsigned char *pkg_checksum_bytes = dnf_package_get_chksum (pkg, &pkg_checksum_type); - g_autofree char *pkg_checksum = hy_chksum_str (pkg_checksum_bytes, pkg_checksum_type); - char *pkg_checksum_with_type = g_strconcat (hy_chksum_name (pkg_checksum_type), ":", pkg_checksum, NULL); - - g_ptr_array_add (pkg_checksums, pkg_checksum_with_type); + g_autofree char* chksum_repr = NULL; + g_assert (rpmostree_get_repodata_chksum_repr (pkg, &chksum_repr, NULL)); + g_ptr_array_add (pkg_checksums, g_steal_pointer (&chksum_repr)); } g_ptr_array_sort (pkg_checksums, rpmostree_ptrarray_sort_compare_strings); diff --git a/src/libpriv/rpmostree-rpm-util.c b/src/libpriv/rpmostree-rpm-util.c index 9b59a27e..c51ba3a5 100644 --- a/src/libpriv/rpmostree-rpm-util.c +++ b/src/libpriv/rpmostree-rpm-util.c @@ -1057,3 +1057,30 @@ rpmostree_fcap_to_xattr_variant (const char *fcap) vfsbytes, FALSE)); return g_variant_ref_sink (g_variant_builder_end (&builder)); } + +/* Returns the checksum of the RPM we retrieved from the repodata XML. The + * actual checksum type used depends on how the repodata was created. Thus, the + * output is a string representation of the form "TYPE:HASH" where TYPE is the + * name of the checksum employed. In most cases, it will be "sha256" (the + * current default for `createrepo_c`). */ +gboolean +rpmostree_get_repodata_chksum_repr (DnfPackage *pkg, + char **out_chksum_repr, + GError **error) +{ + int chksum_type; + g_autofree char *chksum = NULL; + const unsigned char *chksum_raw = NULL; + + chksum_raw = dnf_package_get_chksum (pkg, &chksum_type); + if (chksum_raw) + chksum = hy_chksum_str (chksum_raw, chksum_type); + + if (chksum == NULL) + return glnx_throw (error, "Couldn't get chksum for pkg %s", + dnf_package_get_nevra(pkg)); + + *out_chksum_repr = + g_strconcat (hy_chksum_name (chksum_type), ":", chksum, NULL); + return TRUE; +} diff --git a/src/libpriv/rpmostree-rpm-util.h b/src/libpriv/rpmostree-rpm-util.h index 0368a79d..165483d4 100644 --- a/src/libpriv/rpmostree-rpm-util.h +++ b/src/libpriv/rpmostree-rpm-util.h @@ -166,3 +166,8 @@ typedef enum { char * rpmostree_pkg_custom_nevra_strdup (Header h, RpmOstreePkgNevraFlags flags); + +gboolean +rpmostree_get_repodata_chksum_repr (DnfPackage *pkg, + char **out_chksum_repr, + GError **error); diff --git a/src/libpriv/rpmostree-unpacker.c b/src/libpriv/rpmostree-unpacker.c index ceb341fe..5339cda0 100644 --- a/src/libpriv/rpmostree-unpacker.c +++ b/src/libpriv/rpmostree-unpacker.c @@ -431,7 +431,7 @@ get_lead_sig_header_as_bytes (RpmOstreeUnpacker *self, bytes_remaining); goto out; } - + ret = TRUE; *out_metadata = g_bytes_new_take (g_steal_pointer (&buf), self->cpio_offset); out: @@ -464,10 +464,13 @@ build_metadata_variant (RpmOstreeUnpacker *self, g_auto(GVariantBuilder) metadata_builder; g_variant_builder_init (&metadata_builder, (GVariantType*)"a{sv}"); - /* NB: We store the full header of the RPM in the commit for two reasons: - * first, it holds the file security capabilities, and secondly, we'll need to - * provide it to librpm when it updates the rpmdb (see - * rpmostree_context_assemble_commit()). */ + /* NB: We store the full header of the RPM in the commit for three reasons: + * 1. it holds the file security capabilities, which we need during checkout + * 2. we'll need to provide it to librpm when it updates the rpmdb (see + * rpmostree_context_assemble_commit()) + * 3. it's needed in the local pkgs paths to fool the libdnf stack (see + * rpmostree_context_prepare_install()) + */ { g_autoptr(GBytes) metadata = NULL; @@ -478,8 +481,6 @@ build_metadata_variant (RpmOstreeUnpacker *self, g_variant_new_from_bytes ((GVariantType*)"ay", metadata, TRUE)); - /* NB: the header also includes checksums for every entry in the (cut-off) - * archive, so there's no need to also bring that in */ g_checksum_update (pkg_checksum, g_bytes_get_data (metadata, NULL), g_bytes_get_size (metadata)); @@ -506,7 +507,7 @@ build_metadata_variant (RpmOstreeUnpacker *self, * compatible increments. */ g_variant_builder_add (&metadata_builder, "{sv}", "rpmostree.unpack_minor_version", - g_variant_new_uint32 (2)); + g_variant_new_uint32 (3)); if (self->pkg) { @@ -516,6 +517,16 @@ build_metadata_variant (RpmOstreeUnpacker *self, g_variant_builder_add (&metadata_builder, "{sv}", "rpmostree.repo", repo_metadata_to_variant (repo)); } + + /* include a checksum of the RPM as a whole; the actual algo used depends + * on how the repodata was created, so just keep a repr */ + g_autofree char* chksum_repr = NULL; + if (!rpmostree_get_repodata_chksum_repr (self->pkg, &chksum_repr, error)) + return FALSE; + + g_variant_builder_add (&metadata_builder, "{sv}", + "rpmostree.repodata_checksum", + g_variant_new_string (chksum_repr)); } *out_variant = g_variant_builder_end (&metadata_builder);