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

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