Blob Blame History Raw
From 983aeea57d75494fd4ea2ff2903f966136278c15 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Ale=C5=A1=20Mat=C4=9Bj?= <amatej@redhat.com>
Date: Wed, 9 Feb 2022 13:17:00 +0100
Subject: [PATCH 28/34] Add private API for filling, reading and verifying new
 dnf solv userdata

---
 libdnf/hy-iutil-private.hpp |  24 +++++++++
 libdnf/hy-iutil.cpp         | 102 ++++++++++++++++++++++++++++++++++++
 2 files changed, 126 insertions(+)

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