import libdnf-0.63.0-9.el8

This commit is contained in:
CentOS Sources 2022-05-14 12:12:57 +00:00 committed by Stepan Oksanichenko
parent 4f43d61625
commit dbd5a177a0
11 changed files with 1033 additions and 5682 deletions

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,107 @@
From 7d8f9cfcdf7725fef2c99ecb2dedcdff1e9506d7 Mon Sep 17 00:00:00 2001
From: Jaroslav Rohel <jrohel@redhat.com>
Date: Wed, 13 Apr 2022 12:26:10 +0200
Subject: [PATCH 26/34] context: Substitute all repository config options
(RhBug:2076853)
It also solves the problem: Substitution of variables in `baseurl`
does not work in microdnf and PackageKit unless `metalink` or `mirrorlist`
is set at the same time.
---
libdnf/dnf-repo.cpp | 34 +++++++++++++++++++++++++---------
1 file changed, 25 insertions(+), 9 deletions(-)
diff --git a/libdnf/dnf-repo.cpp b/libdnf/dnf-repo.cpp
index 710045fb..9d42e3e3 100644
--- a/libdnf/dnf-repo.cpp
+++ b/libdnf/dnf-repo.cpp
@@ -83,6 +83,7 @@ typedef struct
LrHandle *repo_handle;
LrResult *repo_result;
LrUrlVars *urlvars;
+ bool unit_test_mode; /* ugly hack for unit tests */
} DnfRepoPrivate;
G_DEFINE_TYPE_WITH_PRIVATE(DnfRepo, dnf_repo, G_TYPE_OBJECT)
@@ -847,8 +848,11 @@ dnf_repo_conf_reset(libdnf::ConfigRepo &config)
/* Loads repository configuration from GKeyFile */
static void
-dnf_repo_conf_from_gkeyfile(libdnf::ConfigRepo &config, const char *repoId, GKeyFile *gkeyFile)
+dnf_repo_conf_from_gkeyfile(DnfRepo *repo, const char *repoId, GKeyFile *gkeyFile)
{
+ DnfRepoPrivate *priv = GET_PRIVATE(repo);
+ auto & config = *priv->repo->getConfig();
+
// Reset to the initial state before reloading the configuration.
dnf_repo_conf_reset(config);
@@ -883,20 +887,31 @@ dnf_repo_conf_from_gkeyfile(libdnf::ConfigRepo &config, const char *repoId, GKey
// list can be ['value1', 'value2, value3'] therefore we first join
// to have 'value1, value2, value3'
g_autofree gchar * tmp_strval = g_strjoinv(",", list);
+
+ // Substitute vars.
+ g_autofree gchar *subst_value = dnf_repo_substitute(repo, tmp_strval);
+
+ if (strcmp(key, "baseurl") == 0 && strstr(tmp_strval, "file://$testdatadir") != NULL) {
+ priv->unit_test_mode = true;
+ }
+
try {
- optionItem.newString(libdnf::Option::Priority::REPOCONFIG, tmp_strval);
+ optionItem.newString(libdnf::Option::Priority::REPOCONFIG, subst_value);
} catch (const std::exception & ex) {
- g_debug("Invalid configuration value: %s = %s in %s; %s", key, value.c_str(), repoId, ex.what());
+ g_debug("Invalid configuration value: %s = %s in %s; %s", key, subst_value, repoId, ex.what());
}
}
} else {
-
// process other (non list) options
+
+ // Substitute vars.
+ g_autofree gchar *subst_value = dnf_repo_substitute(repo, value.c_str());
+
try {
- optionItem.newString(libdnf::Option::Priority::REPOCONFIG, value);
+ optionItem.newString(libdnf::Option::Priority::REPOCONFIG, subst_value);
} catch (const std::exception & ex) {
- g_debug("Invalid configuration value: %s = %s in %s; %s", key, value.c_str(), repoId, ex.what());
+ g_debug("Invalid configuration value: %s = %s in %s; %s", key, subst_value, repoId, ex.what());
}
}
@@ -950,7 +965,7 @@ dnf_repo_set_keyfile_data(DnfRepo *repo, gboolean reloadFromGKeyFile, GError **e
// Reload repository configuration from keyfile.
if (reloadFromGKeyFile) {
- dnf_repo_conf_from_gkeyfile(*conf, repoId, priv->keyfile);
+ dnf_repo_conf_from_gkeyfile(repo, repoId, priv->keyfile);
dnf_repo_apply_setopts(*conf, repoId);
}
@@ -996,8 +1011,9 @@ dnf_repo_set_keyfile_data(DnfRepo *repo, gboolean reloadFromGKeyFile, GError **e
g_autofree gchar *url = NULL;
url = lr_prepend_url_protocol(baseurls[0]);
if (url != NULL && strncasecmp(url, "file://", 7) == 0) {
- if (g_strstr_len(url, -1, "$testdatadir") == NULL)
+ if (!priv->unit_test_mode) {
priv->kind = DNF_REPO_KIND_LOCAL;
+ }
g_free(priv->location);
g_free(priv->keyring);
priv->location = dnf_repo_substitute(repo, url + 7);
@@ -1224,7 +1240,7 @@ dnf_repo_setup(DnfRepo *repo, GError **error) try
auto repoId = priv->repo->getId().c_str();
auto conf = priv->repo->getConfig();
- dnf_repo_conf_from_gkeyfile(*conf, repoId, priv->keyfile);
+ dnf_repo_conf_from_gkeyfile(repo, repoId, priv->keyfile);
dnf_repo_apply_setopts(*conf, repoId);
auto sslverify = conf->sslverify().getValue();
--
2.31.1

View File

@ -0,0 +1,50 @@
From 074ca4cf643c79b8ec3db89a7fd5580ba387eb4d Mon Sep 17 00:00:00 2001
From: Jaroslav Rohel <jrohel@redhat.com>
Date: Wed, 20 Apr 2022 08:22:30 +0200
Subject: [PATCH 27/34] Use environment variable in unittest instead of ugly
hack in libdnf
Libdnf contains hacks for unit tests. This removes one hack.
---
libdnf/dnf-repo.cpp | 3 ---
tests/libdnf/dnf-self-test.c | 3 +++
2 files changed, 3 insertions(+), 3 deletions(-)
diff --git a/libdnf/dnf-repo.cpp b/libdnf/dnf-repo.cpp
index 9d42e3e3..c015d7fd 100644
--- a/libdnf/dnf-repo.cpp
+++ b/libdnf/dnf-repo.cpp
@@ -1191,7 +1191,6 @@ dnf_repo_setup(DnfRepo *repo, GError **error) try
DnfRepoEnabled enabled = DNF_REPO_ENABLED_NONE;
g_autofree gchar *basearch = NULL;
g_autofree gchar *release = NULL;
- g_autofree gchar *testdatadir = NULL;
basearch = g_key_file_get_string(priv->keyfile, "general", "arch", NULL);
if (basearch == NULL)
@@ -1230,8 +1229,6 @@ dnf_repo_setup(DnfRepo *repo, GError **error) try
for (const auto & item : libdnf::dnf_context_get_vars(priv->context))
priv->urlvars = lr_urlvars_set(priv->urlvars, item.first.c_str(), item.second.c_str());
- testdatadir = dnf_realpath(TESTDATADIR);
- priv->urlvars = lr_urlvars_set(priv->urlvars, "testdatadir", testdatadir);
if (!lr_handle_setopt(priv->repo_handle, error, LRO_VARSUB, priv->urlvars))
return FALSE;
if (!lr_handle_setopt(priv->repo_handle, error, LRO_GNUPGHOMEDIR, priv->keyring))
diff --git a/tests/libdnf/dnf-self-test.c b/tests/libdnf/dnf-self-test.c
index 52958371..906f0e21 100644
--- a/tests/libdnf/dnf-self-test.c
+++ b/tests/libdnf/dnf-self-test.c
@@ -1225,6 +1225,9 @@ main(int argc, char **argv)
g_log_set_fatal_mask(NULL, G_LOG_LEVEL_ERROR | G_LOG_LEVEL_CRITICAL);
g_log_set_always_fatal (G_LOG_FATAL_MASK);
+ /* Sets a variable to replace in repository configurations. */
+ g_setenv("DNF_VAR_testdatadir", TESTDATADIR, TRUE);
+
/* tests go here */
g_test_add_func("/libdnf/repo_loader{gpg-asc}", dnf_repo_loader_gpg_asc_func);
g_test_add_func("/libdnf/repo_loader{gpg-wrong-asc}", dnf_repo_loader_gpg_wrong_asc_func);
--
2.31.1

View File

@ -0,0 +1,169 @@
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

View File

@ -0,0 +1,417 @@
From 465a6a59279bd7fa2680c626ca0f10c059276668 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:18:41 +0100
Subject: [PATCH 29/34] Use dnf solv userdata to check versions and checksum
(RhBug:2027445)
Remove unused functions for checksums
= changelog =
msg: Write and check versions and checksums for solvfile cache by using new dnf solvfile userdata (RhBug:2027445)
It is not possible to use old cache files, therefore cache regeneration is triggered automatically.
type: bugfix
resolves: https://bugzilla.redhat.com/show_bug.cgi?id=2027445
---
libdnf/dnf-sack.cpp | 254 ++++++++++++++++++++++--------------
libdnf/hy-iutil-private.hpp | 2 -
libdnf/hy-iutil.cpp | 20 ---
3 files changed, 156 insertions(+), 120 deletions(-)
diff --git a/libdnf/dnf-sack.cpp b/libdnf/dnf-sack.cpp
index b9baeaef..61f4807c 100644
--- a/libdnf/dnf-sack.cpp
+++ b/libdnf/dnf-sack.cpp
@@ -225,17 +225,39 @@ dnf_sack_new(void)
return DNF_SACK(g_object_new(DNF_TYPE_SACK, NULL));
}
-static int
-can_use_repomd_cache(FILE *fp_solv, unsigned char cs_repomd[CHKSUM_BYTES])
-{
- unsigned char cs_cache[CHKSUM_BYTES];
-
- if (fp_solv &&
- !checksum_read(cs_cache, fp_solv) &&
- !checksum_cmp(cs_cache, cs_repomd))
- return 1;
+// Try to load cached solv file into repo otherwise return FALSE
+static gboolean
+try_to_use_cached_solvfile(const char *path, Repo *repo, int flags, const unsigned char *checksum, GError **err){
+ FILE *fp_cache = fopen(path, "r");
+ if (!fp_cache) {
+ // Missing cache files (ENOENT) are not an error and can even be expected in some cases
+ // (such as when repo doesn't have updateinfo/prestodelta metadata).
+ // Use g_debug in order not to pollute the log by default with such entries.
+ if (errno == ENOENT) {
+ g_debug("Failed to open solvfile cache: %s: %s", path, strerror(errno));
+ } else {
+ g_warning("Failed to open solvfile cache: %s: %s", path, strerror(errno));
+ }
+ return FALSE;
+ }
+ std::unique_ptr<SolvUserdata> solv_userdata = solv_userdata_read(fp_cache);
+ gboolean ret = TRUE;
+ if (solv_userdata && solv_userdata_verify(solv_userdata.get(), checksum)) {
+ // after reading the header rewind to the begining
+ fseek(fp_cache, 0, SEEK_SET);
+ if (repo_add_solv(repo, fp_cache, flags)) {
+ g_set_error (err,
+ DNF_ERROR,
+ DNF_ERROR_INTERNAL_ERROR,
+ _("repo_add_solv() has failed."));
+ ret = FALSE;
+ }
+ } else {
+ ret = FALSE;
+ }
- return 0;
+ fclose(fp_cache);
+ return ret;
}
void
@@ -375,33 +397,27 @@ load_ext(DnfSack *sack, HyRepo hrepo, _hy_repo_repodata which_repodata,
gboolean done = FALSE;
char *fn_cache = dnf_sack_give_cache_fn(sack, name, suffix);
- fp = fopen(fn_cache, "r");
assert(libdnf::repoGetImpl(hrepo)->checksum);
- if (can_use_repomd_cache(fp, libdnf::repoGetImpl(hrepo)->checksum)) {
- int flags = 0;
- /* the updateinfo is not a real extension */
- if (which_repodata != _HY_REPODATA_UPDATEINFO)
- flags |= REPO_EXTEND_SOLVABLES;
- /* do not pollute the main pool with directory component ids */
- if (which_repodata == _HY_REPODATA_FILENAMES || which_repodata == _HY_REPODATA_OTHER)
- flags |= REPO_LOCALPOOL;
- done = TRUE;
+
+ int flags = 0;
+ /* the updateinfo is not a real extension */
+ if (which_repodata != _HY_REPODATA_UPDATEINFO)
+ flags |= REPO_EXTEND_SOLVABLES;
+ /* do not pollute the main pool with directory component ids */
+ if (which_repodata == _HY_REPODATA_FILENAMES || which_repodata == _HY_REPODATA_OTHER)
+ flags |= REPO_LOCALPOOL;
+ if (try_to_use_cached_solvfile(fn_cache, repo, flags, libdnf::repoGetImpl(hrepo)->checksum, error)) {
g_debug("%s: using cache file: %s", __func__, fn_cache);
- ret = repo_add_solv(repo, fp, flags);
- if (ret) {
- g_set_error_literal (error,
- DNF_ERROR,
- DNF_ERROR_INTERNAL_ERROR,
- _("failed to add solv"));
- return FALSE;
- } else {
- repo_update_state(hrepo, which_repodata, _HY_LOADED_CACHE);
- repo_set_repodata(hrepo, which_repodata, repo->nrepodata - 1);
- }
+ done = TRUE;
+ repo_update_state(hrepo, which_repodata, _HY_LOADED_CACHE);
+ repo_set_repodata(hrepo, which_repodata, repo->nrepodata - 1);
}
+ if (error && *error) {
+ g_prefix_error(error, _("Loading extension cache %s (%d) failed: "), fn_cache, which_repodata);
+ return FALSE;
+ }
+
g_free(fn_cache);
- if (fp)
- fclose(fp);
if (done)
return TRUE;
@@ -514,35 +530,53 @@ write_main(DnfSack *sack, HyRepo hrepo, int switchtosolv, GError **error)
strerror(errno));
goto done;
}
- rc = repo_write(repo, fp);
- rc |= checksum_write(repoImpl->checksum, fp);
- rc |= fclose(fp);
+
+ SolvUserdata solv_userdata;
+ if (solv_userdata_fill(&solv_userdata, repoImpl->checksum, error)) {
+ ret = FALSE;
+ fclose(fp);
+ goto done;
+ }
+
+ Repowriter *writer = repowriter_create(repo);
+ repowriter_set_userdata(writer, &solv_userdata, solv_userdata_size);
+ rc = repowriter_write(writer, fp);
+ repowriter_free(writer);
if (rc) {
+ ret = FALSE;
+ fclose(fp);
+ g_set_error(error,
+ DNF_ERROR,
+ DNF_ERROR_INTERNAL_ERROR,
+ _("While writing primary cache %s repowriter write failed: %i, error: %s"),
+ tmp_fn_templ, rc, pool_errstr(repo->pool));
+ goto done;
+ }
+
+ if (fclose(fp)) {
ret = FALSE;
g_set_error (error,
DNF_ERROR,
DNF_ERROR_FILE_INVALID,
- _("write_main() failed writing data: %i"), rc);
+ _("Failed closing tmp file %s: %s"),
+ tmp_fn_templ, strerror(errno));
goto done;
}
}
if (switchtosolv && repo_is_one_piece(repo)) {
+ repo_empty(repo, 1);
/* switch over to written solv file activate paging */
- FILE *fp = fopen(tmp_fn_templ, "r");
- if (fp) {
- repo_empty(repo, 1);
- rc = repo_add_solv(repo, fp, 0);
- fclose(fp);
- if (rc) {
- /* this is pretty fatal */
- ret = FALSE;
- g_set_error_literal (error,
- DNF_ERROR,
- DNF_ERROR_FILE_INVALID,
- _("write_main() failed to re-load "
- "written solv file"));
- goto done;
- }
+ gboolean loaded = try_to_use_cached_solvfile(tmp_fn_templ, repo, 0, repoImpl->checksum, error);
+ if (error && *error) {
+ g_prefix_error(error, _("Failed to use newly written primary cache: %s: "), tmp_fn_templ);
+ ret = FALSE;
+ goto done;
+ }
+ if (!loaded) {
+ g_set_error(error, DNF_ERROR, DNF_ERROR_INTERNAL_ERROR,
+ _("Failed to use newly written primary cache: %s"), tmp_fn_templ);
+ ret = FALSE;
+ goto done;
}
}
@@ -569,20 +603,6 @@ write_ext_updateinfo_filter(Repo *repo, Repokey *key, void *kfdata)
return repo_write_stdkeyfilter(repo, key, 0);
}
-static int
-write_ext_updateinfo(HyRepo hrepo, Repodata *data, FILE *fp)
-{
- auto repoImpl = libdnf::repoGetImpl(hrepo);
- Repo *repo = repoImpl->libsolvRepo;
- int oldstart = repo->start;
- repo->start = repoImpl->main_end;
- repo->nsolvables -= repoImpl->main_nsolvables;
- int res = repo_write_filtered(repo, fp, write_ext_updateinfo_filter, data, 0);
- repo->start = oldstart;
- repo->nsolvables += repoImpl->main_nsolvables;
- return res;
-}
-
static gboolean
write_ext(DnfSack *sack, HyRepo hrepo, _hy_repo_repodata which_repodata,
const char *suffix, GError **error)
@@ -611,37 +631,78 @@ write_ext(DnfSack *sack, HyRepo hrepo, _hy_repo_repodata which_repodata,
FILE *fp = fdopen(tmp_fd, "w+");
g_debug("%s: storing %s to: %s", __func__, repo->name, tmp_fn_templ);
- if (which_repodata != _HY_REPODATA_UPDATEINFO)
- ret |= repodata_write(data, fp);
- else
- ret |= write_ext_updateinfo(hrepo, data, fp);
- ret |= checksum_write(repoImpl->checksum, fp);
- ret |= fclose(fp);
+
+ SolvUserdata solv_userdata;
+ if (solv_userdata_fill(&solv_userdata, repoImpl->checksum, error)) {
+ fclose(fp);
+ success = FALSE;
+ goto done;
+ }
+
+ Repowriter *writer = repowriter_create(repo);
+ repowriter_set_userdata(writer, &solv_userdata, solv_userdata_size);
+ if (which_repodata != _HY_REPODATA_UPDATEINFO) {
+ repowriter_set_repodatarange(writer, data->repodataid, data->repodataid + 1);
+ repowriter_set_flags(writer, REPOWRITER_NO_STORAGE_SOLVABLE);
+ ret = repowriter_write(writer, fp);
+ } else {
+ // write only updateinfo repodata
+ int oldstart = repo->start;
+ repo->start = repoImpl->main_end;
+ repo->nsolvables -= repoImpl->main_nsolvables;
+ repowriter_set_flags(writer, REPOWRITER_LEGACY);
+ repowriter_set_keyfilter(writer, write_ext_updateinfo_filter, data);
+ repowriter_set_keyqueue(writer, 0);
+ ret = repowriter_write(writer, fp);
+ repo->start = oldstart;
+ repo->nsolvables += repoImpl->main_nsolvables;
+ }
+ repowriter_free(writer);
if (ret) {
+ success = FALSE;
+ fclose(fp);
+ g_set_error (error,
+ DNF_ERROR,
+ DNF_ERROR_INTERNAL_ERROR,
+ _("While writing extension cache %s (%d): repowriter write failed: %i, error: %s"),
+ tmp_fn_templ, which_repodata, ret, pool_errstr(repo->pool));
+ goto done;
+ }
+
+ if (fclose(fp)) {
success = FALSE;
g_set_error (error,
DNF_ERROR,
- DNF_ERROR_FAILED,
- _("write_ext(%1$d) has failed: %2$d"),
- which_repodata, ret);
+ DNF_ERROR_FILE_INVALID,
+ _("While writing extension cache (%d): cannot close temporary file: %s"),
+ which_repodata, tmp_fn_templ);
goto done;
}
}
if (repo_is_one_piece(repo) && which_repodata != _HY_REPODATA_UPDATEINFO) {
/* switch over to written solv file activate paging */
- FILE *fp = fopen(tmp_fn_templ, "r");
- if (fp) {
- int flags = REPO_USE_LOADING | REPO_EXTEND_SOLVABLES;
- /* do not pollute the main pool with directory component ids */
- if (which_repodata == _HY_REPODATA_FILENAMES || which_repodata == _HY_REPODATA_OTHER)
- flags |= REPO_LOCALPOOL;
- repodata_extend_block(data, repo->start, repo->end - repo->start);
- data->state = REPODATA_LOADING;
- repo_add_solv(repo, fp, flags);
- data->state = REPODATA_AVAILABLE;
- fclose(fp);
+ int flags = REPO_USE_LOADING | REPO_EXTEND_SOLVABLES;
+ /* do not pollute the main pool with directory component ids */
+ if (which_repodata == _HY_REPODATA_FILENAMES || which_repodata == _HY_REPODATA_OTHER)
+ flags |= REPO_LOCALPOOL;
+ repodata_extend_block(data, repo->start, repo->end - repo->start);
+ data->state = REPODATA_LOADING;
+ int loaded = try_to_use_cached_solvfile(tmp_fn_templ, repo, flags, repoImpl->checksum, error);
+ if (error && *error) {
+ g_prefix_error(error, _("Failed to use newly written extension cache: %s (%d): "),
+ tmp_fn_templ, which_repodata);
+ success = FALSE;
+ goto done;
+ }
+ if (!loaded) {
+ g_set_error(error, DNF_ERROR, DNF_ERROR_INTERNAL_ERROR,
+ _("Failed to use newly written extension cache: %s (%d)"), tmp_fn_templ, which_repodata);
+ success = FALSE;
+ goto done;
}
+
+ data->state = REPODATA_AVAILABLE;
}
if (!mv(tmp_fn_templ, fn, error)) {
@@ -672,7 +733,7 @@ load_yum_repo(DnfSack *sack, HyRepo hrepo, GError **error)
FILE *fp_primary = NULL;
FILE *fp_repomd = NULL;
- FILE *fp_cache = fopen(fn_cache, "r");
+
if (!fn_repomd) {
g_set_error (error,
DNF_ERROR,
@@ -693,18 +754,17 @@ load_yum_repo(DnfSack *sack, HyRepo hrepo, GError **error)
}
checksum_fp(repoImpl->checksum, fp_repomd);
- if (can_use_repomd_cache(fp_cache, repoImpl->checksum)) {
+ if (try_to_use_cached_solvfile(fn_cache, repo, 0, repoImpl->checksum, error)) {
const char *chksum = pool_checksum_str(pool, repoImpl->checksum);
g_debug("using cached %s (0x%s)", name, chksum);
- if (repo_add_solv(repo, fp_cache, 0)) {
- g_set_error (error,
- DNF_ERROR,
- DNF_ERROR_INTERNAL_ERROR,
- _("repo_add_solv() has failed."));
- retval = FALSE;
- goto out;
- }
repoImpl->state_main = _HY_LOADED_CACHE;
+ goto out;
+ }
+
+ if (error && *error) {
+ g_prefix_error(error, _("While loading repository failed to use %s: "), fn_cache);
+ retval = FALSE;
+ goto out;
} else {
auto primary = hrepo->getMetadataPath(MD_TYPE_PRIMARY);
if (primary.empty()) {
@@ -733,8 +793,6 @@ load_yum_repo(DnfSack *sack, HyRepo hrepo, GError **error)
repoImpl->state_main = _HY_LOADED_FETCH;
}
out:
- if (fp_cache)
- fclose(fp_cache);
if (fp_repomd)
fclose(fp_repomd);
if (fp_primary)
diff --git a/libdnf/hy-iutil-private.hpp b/libdnf/hy-iutil-private.hpp
index d498c032..efc91c63 100644
--- a/libdnf/hy-iutil-private.hpp
+++ b/libdnf/hy-iutil-private.hpp
@@ -52,9 +52,7 @@ int solv_userdata_verify(const SolvUserdata *solv_userdata, const unsigned char
/* crypto utils */
int checksum_cmp(const unsigned char *cs1, const unsigned char *cs2);
int checksum_fp(unsigned char *out, FILE *fp);
-int checksum_read(unsigned char *csout, FILE *fp);
int checksum_stat(unsigned char *out, FILE *fp);
-int checksum_write(const unsigned char *cs, FILE *fp);
int checksumt_l2h(int type);
const char *pool_checksum_str(Pool *pool, const unsigned char *chksum);
diff --git a/libdnf/hy-iutil.cpp b/libdnf/hy-iutil.cpp
index f81ca52f..c409a10a 100644
--- a/libdnf/hy-iutil.cpp
+++ b/libdnf/hy-iutil.cpp
@@ -142,17 +142,6 @@ checksum_fp(unsigned char *out, FILE *fp)
return 0;
}
-/* calls rewind(fp) before returning */
-int
-checksum_read(unsigned char *csout, FILE *fp)
-{
- if (fseek(fp, -32, SEEK_END) ||
- fread(csout, CHKSUM_BYTES, 1, fp) != 1)
- return 1;
- rewind(fp);
- return 0;
-}
-
/* does not move the fp position */
int
checksum_stat(unsigned char *out, FILE *fp)
@@ -174,15 +163,6 @@ checksum_stat(unsigned char *out, FILE *fp)
return 0;
}
-/* moves fp to the end of file */
-int checksum_write(const unsigned char *cs, FILE *fp)
-{
- if (fseek(fp, 0, SEEK_END) ||
- fwrite(cs, CHKSUM_BYTES, 1, fp) != 1)
- return 1;
- return 0;
-}
-
static std::array<char, solv_userdata_solv_toolversion_size>
get_padded_solv_toolversion()
{
--
2.31.1

View File

@ -0,0 +1,83 @@
From 1e0f8f66f6ff30e177c41be7d72330d5eccf2ff8 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:24:06 +0100
Subject: [PATCH 30/34] Update unittest to test the new private dnf solvfile
API
---
tests/hawkey/test_iutil.cpp | 34 ++++++++++++++++++++++------------
1 file changed, 22 insertions(+), 12 deletions(-)
diff --git a/tests/hawkey/test_iutil.cpp b/tests/hawkey/test_iutil.cpp
index 8d00cc94..f3c04782 100644
--- a/tests/hawkey/test_iutil.cpp
+++ b/tests/hawkey/test_iutil.cpp
@@ -24,6 +24,8 @@
#include <solv/pool.h>
+#include <solv/repo.h>
+#include <solv/repo_write.h>
#include "libdnf/hy-util.h"
@@ -97,28 +99,36 @@ START_TEST(test_checksum)
}
END_TEST
-START_TEST(test_checksum_write_read)
+START_TEST(test_dnf_solvfile_userdata)
{
char *new_file = solv_dupjoin(test_globals.tmpdir,
- "/test_checksum_write_read", NULL);
+ "/test_dnf_solvfile_userdata", NULL);
build_test_file(new_file);
unsigned char cs_computed[CHKSUM_BYTES];
- unsigned char cs_read[CHKSUM_BYTES];
- FILE *fp = fopen(new_file, "r");
+ FILE *fp = fopen(new_file, "r+");
checksum_fp(cs_computed, fp);
- // fails, file opened read-only:
- fail_unless(checksum_write(cs_computed, fp) == 1);
- fclose(fp);
- fp = fopen(new_file, "r+");
- fail_if(checksum_write(cs_computed, fp));
+
+ SolvUserdata solv_userdata;
+ fail_if(solv_userdata_fill(&solv_userdata, cs_computed, NULL));
+
+ Pool *pool = pool_create();
+ Repo *repo = repo_create(pool, "test_repo");
+ Repowriter *writer = repowriter_create(repo);
+ repowriter_set_userdata(writer, &solv_userdata, solv_userdata_size);
+ fail_if(repowriter_write(writer, fp));
+ repowriter_free(writer);
fclose(fp);
+
fp = fopen(new_file, "r");
- fail_if(checksum_read(cs_read, fp));
- fail_if(checksum_cmp(cs_computed, cs_read));
+ std::unique_ptr<SolvUserdata> dnf_solvfile = solv_userdata_read(fp);
+ fail_unless(dnf_solvfile);
+ fail_unless(solv_userdata_verify(dnf_solvfile.get(), cs_computed));
fclose(fp);
g_free(new_file);
+ repo_free(repo, 0);
+ pool_free(pool);
}
END_TEST
@@ -181,7 +191,7 @@ iutil_suite(void)
TCase *tc = tcase_create("Main");
tcase_add_test(tc, test_abspath);
tcase_add_test(tc, test_checksum);
- tcase_add_test(tc, test_checksum_write_read);
+ tcase_add_test(tc, test_dnf_solvfile_userdata);
tcase_add_test(tc, test_mkcachedir);
tcase_add_test(tc, test_version_split);
suite_add_tcase(s, tc);
--
2.31.1

View File

@ -0,0 +1,38 @@
From 893eb087e56588d62e81e91e5d283003bd80552a Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Ale=C5=A1=20Mat=C4=9Bj?= <amatej@redhat.com>
Date: Tue, 8 Mar 2022 11:43:38 +0100
Subject: [PATCH 31/34] Increase required libsolv version for cache versioning
---
CMakeLists.txt | 2 +-
libdnf.spec | 4 ++--
2 files changed, 3 insertions(+), 3 deletions(-)
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 60cf1b8c..d895b2bf 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -51,7 +51,7 @@ endif()
# build dependencies
find_package(Gpgme REQUIRED)
-find_package(LibSolv 0.6.30 REQUIRED COMPONENTS ext)
+find_package(LibSolv 0.7.20 REQUIRED COMPONENTS ext)
find_package(OpenSSL REQUIRED)
diff --git a/libdnf.spec b/libdnf.spec
index a849cdea..aa51dd28 100644
--- a/libdnf.spec
+++ b/libdnf.spec
@@ -1,5 +1,5 @@
-%global libsolv_version 0.7.17
-%global libmodulemd_version 2.11.2-2
+%global libsolv_version 0.7.21
+%global libmodulemd_version 2.13.0
%global librepo_version 1.13.1
%global dnf_conflict 4.3.0
%global swig_version 3.0.12
--
2.31.1

View File

@ -0,0 +1,46 @@
From b636af779fcdab326eef7bbb74912254c2fa2b0c Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Ale=C5=A1=20Mat=C4=9Bj?= <amatej@redhat.com>
Date: Thu, 17 Mar 2022 10:34:24 +0100
Subject: [PATCH 32/34] Add more specific error handling for loading repomd and
primary
---
libdnf/dnf-sack.cpp | 19 +++++++++++++++----
1 file changed, 15 insertions(+), 4 deletions(-)
diff --git a/libdnf/dnf-sack.cpp b/libdnf/dnf-sack.cpp
index 61f4807c..8e11b8f8 100644
--- a/libdnf/dnf-sack.cpp
+++ b/libdnf/dnf-sack.cpp
@@ -780,13 +780,24 @@ load_yum_repo(DnfSack *sack, HyRepo hrepo, GError **error)
fp_primary = solv_xfopen(primary.c_str(), "r");
assert(fp_primary);
- g_debug("fetching %s", name);
- if (repo_add_repomdxml(repo, fp_repomd, 0) || \
- repo_add_rpmmd(repo, fp_primary, 0, 0)) {
+ g_debug("Loading repomd: %s", fn_repomd);
+ if (repo_add_repomdxml(repo, fp_repomd, 0)) {
g_set_error (error,
DNF_ERROR,
DNF_ERROR_INTERNAL_ERROR,
- _("repo_add_repomdxml/rpmmd() has failed."));
+ _("Loading repomd has failed: %s"),
+ pool_errstr(repo->pool));
+ retval = FALSE;
+ goto out;
+ }
+
+ g_debug("Loading primary: %s", primary.c_str());
+ if (repo_add_rpmmd(repo, fp_primary, 0, 0)) {
+ g_set_error (error,
+ DNF_ERROR,
+ DNF_ERROR_INTERNAL_ERROR,
+ _("Loading primary has failed: %s"),
+ pool_errstr(repo->pool));
retval = FALSE;
goto out;
}
--
2.31.1

View File

@ -0,0 +1,74 @@
From c5919efe898294420ec8e91e4eed5b9081e681c5 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Luk=C3=A1=C5=A1=20Hr=C3=A1zk=C3=BD?= <lhrazky@redhat.com>
Date: Thu, 17 Feb 2022 18:18:16 +0100
Subject: [PATCH 33/34] libdnf/transaction/RPMItem: Fix handling transaction id
in resolveTransactionItemReason
The maxTransactionId argument was ignored, the method was always returning the
reason from the last transaction. This is the correct result for
maxTransactionId = -1. In a couple of places the method is called with
maxTransactionId = -2. Fixing this would mean nontrivial changes to the
logic which could potentially break something else, so I'm leaving this
behavior unchanged.
For non-negative values of maxTransactionId (with which it's not being called
anywhere in dnf codebase), the commit adds a condition to SELECT only
transaction ids less than or equal to maxTransactionId.
= changelog =
msg: Fix handling transaction id in resolveTransactionItemReason
type: bugfix
resolves: https://bugzilla.redhat.com/show_bug.cgi?id=2053014
resolves: https://bugzilla.redhat.com/show_bug.cgi?id=2010259
---
libdnf/transaction/RPMItem.cpp | 21 ++++++++++++++++++---
1 file changed, 18 insertions(+), 3 deletions(-)
diff --git a/libdnf/transaction/RPMItem.cpp b/libdnf/transaction/RPMItem.cpp
index 5f667ab9..ecce789d 100644
--- a/libdnf/transaction/RPMItem.cpp
+++ b/libdnf/transaction/RPMItem.cpp
@@ -255,7 +255,11 @@ RPMItem::resolveTransactionItemReason(SQLite3Ptr conn,
const std::string &arch,
int64_t maxTransactionId)
{
- const char *sql = R"**(
+ // NOTE: All negative maxTransactionId values are treated the same. The
+ // method is called with maxTransactionId = -2 in a couple of places, the
+ // semantics here have been the same as with -1 for a long time. If it
+ // ain't broke...
+ std::string sql = R"**(
SELECT
ti.action as action,
ti.reason as reason
@@ -271,14 +275,25 @@ RPMItem::resolveTransactionItemReason(SQLite3Ptr conn,
AND ti.action not in (3, 5, 7, 10)
AND i.name = ?
AND i.arch = ?
+ )**";
+
+ if (maxTransactionId >= 0) {
+ sql.append(" AND ti.trans_id <= ?");
+ }
+
+ sql.append(R"**(
ORDER BY
ti.trans_id DESC
LIMIT 1
- )**";
+ )**");
if (arch != "") {
SQLite3::Query query(*conn, sql);
- query.bindv(name, arch);
+ if (maxTransactionId >= 0) {
+ query.bindv(name, arch, maxTransactionId);
+ } else {
+ query.bindv(name, arch);
+ }
if (query.step() == SQLite3::Statement::StepResult::ROW) {
auto action = static_cast< TransactionItemAction >(query.get< int64_t >("action"));
--
2.31.1

View File

@ -0,0 +1,33 @@
From c303b7c3723f3e9fbc43963a62237ea17516fc6b Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Luk=C3=A1=C5=A1=20Hr=C3=A1zk=C3=BD?= <lhrazky@redhat.com>
Date: Thu, 17 Feb 2022 18:30:14 +0100
Subject: [PATCH 34/34] libdnf/transaction/TransactionItem: Set short action
for Reason Change
Sets the "short" (one letter) representation of the Reason Change action
to "C".
This was likely not ever used before as the only way to create a
transaction with a reason change and something else is rolling back
multiple transactions, which was broken.
---
libdnf/transaction/TransactionItem.cpp | 3 +--
1 file changed, 1 insertion(+), 2 deletions(-)
diff --git a/libdnf/transaction/TransactionItem.cpp b/libdnf/transaction/TransactionItem.cpp
index 3b43d1f1..4358038e 100644
--- a/libdnf/transaction/TransactionItem.cpp
+++ b/libdnf/transaction/TransactionItem.cpp
@@ -51,8 +51,7 @@ static const std::map< TransactionItemAction, std::string > transactionItemActio
{TransactionItemAction::REMOVE, "E"},
{TransactionItemAction::REINSTALL, "R"},
{TransactionItemAction::REINSTALLED, "R"},
- // TODO: replace "?" with something better
- {TransactionItemAction::REASON_CHANGE, "?"},
+ {TransactionItemAction::REASON_CHANGE, "C"},
};
/*
--
2.31.1

View File

@ -1,4 +1,4 @@
%global libsolv_version 0.7.17
%global libsolv_version 0.7.20-3
%global libmodulemd_version 2.11.2-2
%global librepo_version 1.13.1
%global dnf_conflict 4.3.0
@ -56,7 +56,7 @@
Name: libdnf
Version: %{libdnf_major_version}.%{libdnf_minor_version}.%{libdnf_micro_version}
Release: 8%{?dist}
Release: 9%{?dist}
Summary: Library providing simplified C and Python API to libsolv
License: LGPLv2+
URL: https://github.com/rpm-software-management/libdnf
@ -86,7 +86,15 @@ Patch22: 0022-hawkey-surrogateescape-error-handler-to-decode-UTF-8-string
Patch23: 0023-Turn-off-strict-validation-of-modulemd-documents-RhBug200485320071662007167.patch
Patch24: 0024-Add-unittest-for-setting-up-repo-with-empty-keyfile-RhBug1994614.patch
Patch25: 0025-Add-getLatestModules.patch
Patch26: 0026-Update-translations-RhBug-2017272.patch
Patch26: 0026-context-Substitute-all-repository-config-options-RhB.patch
Patch27: 0027-Use-environment-variable-in-unittest-instead-of-ugly.patch
Patch28: 0028-Add-private-API-for-filling-reading-and-verifying-ne.patch
Patch29: 0029-Use-dnf-solv-userdata-to-check-versions-and-checksum.patch
Patch30: 0030-Update-unittest-to-test-the-new-private-dnf-solvfile.patch
Patch31: 0031-Increase-required-libsolv-version-for-cache-versioni.patch
Patch32: 0032-Add-more-specific-error-handling-for-loading-repomd-.patch
Patch33: 0033-libdnf-transaction-RPMItem-Fix-handling-transaction-.patch
Patch34: 0034-libdnf-transaction-TransactionItem-Set-short-action-.patch
BuildRequires: cmake
BuildRequires: gcc
@ -331,8 +339,11 @@ popd
%endif
%changelog
* Fri Mar 18 2022 Marek Blaha <mblaha@redhat.com> - 0.63.0-8
- Update translations
* Wed May 04 2022 Lukas Hrazky <lhrazky@redhat.com> - 0.63.0-8
- Substitute all repository config options (fixes substitution of baseurl)
- Use solvfile userdata to store and check checksums and solv versions
- Fix handling transaction id in resolveTransactionItemReason
- Set short action for Reason Change
* Fri Jan 14 2022 Pavla Kratochvilova <pkratoch@redhat.com> - 0.63.0-7
- Rebuild with new release number