Blame SOURCES/0028-Add-private-API-for-filling-reading-and-verifying-ne.patch

1a8671
From 983aeea57d75494fd4ea2ff2903f966136278c15 Mon Sep 17 00:00:00 2001
1a8671
From: =?UTF-8?q?Ale=C5=A1=20Mat=C4=9Bj?= <amatej@redhat.com>
1a8671
Date: Wed, 9 Feb 2022 13:17:00 +0100
1a8671
Subject: [PATCH 28/34] Add private API for filling, reading and verifying new
1a8671
 dnf solv userdata
1a8671
1a8671
---
1a8671
 libdnf/hy-iutil-private.hpp |  24 +++++++++
1a8671
 libdnf/hy-iutil.cpp         | 102 ++++++++++++++++++++++++++++++++++++
1a8671
 2 files changed, 126 insertions(+)
1a8671
1a8671
diff --git a/libdnf/hy-iutil-private.hpp b/libdnf/hy-iutil-private.hpp
1a8671
index e07b1b51..d498c032 100644
1a8671
--- a/libdnf/hy-iutil-private.hpp
1a8671
+++ b/libdnf/hy-iutil-private.hpp
1a8671
@@ -24,6 +24,30 @@
1a8671
 #include "hy-iutil.h"
1a8671
 #include "hy-types.h"
1a8671
 #include "sack/packageset.hpp"
1a8671
+#include <array>
1a8671
+#include <utility>
1a8671
+
1a8671
+// Use 8 bytes for libsolv version (API: solv_toolversion)
1a8671
+// to be future proof even though it currently is "1.2"
1a8671
+static constexpr const size_t solv_userdata_solv_toolversion_size{8};
1a8671
+static constexpr const std::array<char, 4> solv_userdata_magic{'\0', 'd', 'n', 'f'};
1a8671
+static constexpr const std::array<char, 4> solv_userdata_dnf_version{'\0', '1', '.', '0'};
1a8671
+
1a8671
+static constexpr const int solv_userdata_size = solv_userdata_solv_toolversion_size + \
1a8671
+                                                   solv_userdata_magic.size() + \
1a8671
+                                                   solv_userdata_dnf_version.size() + \
1a8671
+                                                   CHKSUM_BYTES;
1a8671
+
1a8671
+struct SolvUserdata {
1a8671
+    char dnf_magic[solv_userdata_magic.size()];
1a8671
+    char dnf_version[solv_userdata_dnf_version.size()];
1a8671
+    char libsolv_version[solv_userdata_solv_toolversion_size];
1a8671
+    unsigned char checksum[CHKSUM_BYTES];
1a8671
+}__attribute__((packed)); ;
1a8671
+
1a8671
+int solv_userdata_fill(SolvUserdata *solv_userdata, const unsigned char *checksum, GError** error);
1a8671
+std::unique_ptr<SolvUserdata> solv_userdata_read(FILE *fp);
1a8671
+int solv_userdata_verify(const SolvUserdata *solv_userdata, const unsigned char *checksum);
1a8671
 
1a8671
 /* crypto utils */
1a8671
 int checksum_cmp(const unsigned char *cs1, const unsigned char *cs2);
1a8671
diff --git a/libdnf/hy-iutil.cpp b/libdnf/hy-iutil.cpp
1a8671
index 2af13197..f81ca52f 100644
1a8671
--- a/libdnf/hy-iutil.cpp
1a8671
+++ b/libdnf/hy-iutil.cpp
1a8671
@@ -43,6 +43,7 @@ extern "C" {
1a8671
 #include <solv/evr.h>
1a8671
 #include <solv/solver.h>
1a8671
 #include <solv/solverdebug.h>
1a8671
+#include <solv/repo_solv.h>
1a8671
 #include <solv/util.h>
1a8671
 #include <solv/pool_parserpmrichdep.h>
1a8671
 }
1a8671
@@ -182,6 +183,107 @@ int checksum_write(const unsigned char *cs, FILE *fp)
1a8671
     return 0;
1a8671
 }
1a8671
 
1a8671
+static std::array<char, solv_userdata_solv_toolversion_size>
1a8671
+get_padded_solv_toolversion()
1a8671
+{
1a8671
+    std::array<char, solv_userdata_solv_toolversion_size> padded_solv_toolversion{};
1a8671
+    std::string solv_ver_str{solv_toolversion};
1a8671
+    std::copy(solv_ver_str.rbegin(), solv_ver_str.rend(), padded_solv_toolversion.rbegin());
1a8671
+
1a8671
+    return padded_solv_toolversion;
1a8671
+}
1a8671
+
1a8671
+int
1a8671
+solv_userdata_fill(SolvUserdata *solv_userdata, const unsigned char *checksum, GError** error)
1a8671
+{
1a8671
+    if (strlen(solv_toolversion) > solv_userdata_solv_toolversion_size) {
1a8671
+        g_set_error(error, DNF_ERROR, DNF_ERROR_INTERNAL_ERROR,
1a8671
+                    _("Libsolv's solv_toolversion is: %zu long but we expect max of: %zu"),
1a8671
+                    strlen(solv_toolversion), solv_userdata_solv_toolversion_size);
1a8671
+        return 1;
1a8671
+    }
1a8671
+
1a8671
+    // copy dnf solv file magic
1a8671
+    memcpy(solv_userdata->dnf_magic, solv_userdata_magic.data(), solv_userdata_magic.size());
1a8671
+
1a8671
+    // copy dnf solv file version
1a8671
+    memcpy(solv_userdata->dnf_version, solv_userdata_dnf_version.data(), solv_userdata_dnf_version.size());
1a8671
+
1a8671
+    // copy libsolv solv file version
1a8671
+    memcpy(solv_userdata->libsolv_version, get_padded_solv_toolversion().data(), solv_userdata_solv_toolversion_size);
1a8671
+
1a8671
+    // copy checksum
1a8671
+    memcpy(solv_userdata->checksum, checksum, CHKSUM_BYTES);
1a8671
+
1a8671
+    return 0;
1a8671
+}
1a8671
+
1a8671
+
1a8671
+std::unique_ptr<SolvUserdata>
1a8671
+solv_userdata_read(FILE *fp)
1a8671
+{
1a8671
+    unsigned char *dnf_solvfile_userdata_read = NULL;
1a8671
+    int dnf_solvfile_userdata_len_read;
1a8671
+    if (!fp) {
1a8671
+        return nullptr;
1a8671
+    }
1a8671
+
1a8671
+    int ret_code = solv_read_userdata(fp, &dnf_solvfile_userdata_read, &dnf_solvfile_userdata_len_read);
1a8671
+    // The userdata layout has to match our struct exactly so we can just cast the memory
1a8671
+    // allocated by libsolv
1a8671
+    std::unique_ptr<SolvUserdata> uniq_userdata(reinterpret_cast<SolvUserdata *>(dnf_solvfile_userdata_read));
1a8671
+    if(ret_code) {
1a8671
+        g_warning("Failed to read solv userdata: solv_read_userdata returned: %i", ret_code);
1a8671
+        return nullptr;
1a8671
+    }
1a8671
+
1a8671
+    if (dnf_solvfile_userdata_len_read != solv_userdata_size) {
1a8671
+        g_warning("Solv userdata length mismatch, read: %i vs expected: %i",
1a8671
+                  dnf_solvfile_userdata_len_read, solv_userdata_size);
1a8671
+        return nullptr;
1a8671
+    }
1a8671
+
1a8671
+    return uniq_userdata;
1a8671
+}
1a8671
+
1a8671
+gboolean
1a8671
+solv_userdata_verify(const SolvUserdata *solv_userdata, const unsigned char *checksum)
1a8671
+{
1a8671
+    // check dnf solvfile magic bytes
1a8671
+    if (memcmp(solv_userdata->dnf_magic, solv_userdata_magic.data(), solv_userdata_magic.size()) != 0) {
1a8671
+        // This is not dnf header do not read after it
1a8671
+        g_warning("magic bytes don't match, read: %s vs. dnf solvfile magic: %s",
1a8671
+                  solv_userdata->dnf_magic, solv_userdata_magic.data());
1a8671
+        return FALSE;
1a8671
+    }
1a8671
+
1a8671
+    // check dnf solvfile version
1a8671
+    if (memcmp(solv_userdata->dnf_version, solv_userdata_dnf_version.data(), solv_userdata_dnf_version.size()) != 0) {
1a8671
+        // Mismatching dnf solvfile version -> we need to regenerate
1a8671
+        g_warning("dnf solvfile version doesn't match, read: %s vs. dnf solvfile version: %s",
1a8671
+                  solv_userdata->dnf_version, solv_userdata_dnf_version.data());
1a8671
+        return FALSE;
1a8671
+    }
1a8671
+
1a8671
+    // check libsolv solvfile version
1a8671
+    if (memcmp(solv_userdata->libsolv_version, get_padded_solv_toolversion().data(), solv_userdata_solv_toolversion_size) != 0) {
1a8671
+        // Mismatching libsolv solvfile version -> we need to regenerate
1a8671
+        g_warning("libsolv solvfile version doesn't match, read: %s vs. libsolv version: %s",
1a8671
+                  solv_userdata->libsolv_version, solv_toolversion);
1a8671
+        return FALSE;
1a8671
+    }
1a8671
+
1a8671
+    // check solvfile checksum
1a8671
+    if (checksum_cmp(solv_userdata->checksum, checksum)) {
1a8671
+        // Mismatching solvfile checksum -> we need to regenerate
1a8671
+        g_debug("solvfile checksum doesn't match, read: %s vs. repomd checksum: %s",
1a8671
+                solv_userdata->checksum, checksum);
1a8671
+        return FALSE;
1a8671
+    }
1a8671
+
1a8671
+    return TRUE;
1a8671
+}
1a8671
+
1a8671
 int
1a8671
 checksum_type2length(int type)
1a8671
 {
1a8671
-- 
1a8671
2.31.1
1a8671