From 6e236af4f9450f766eb7e92f6f1053f3cc8034e8 Mon Sep 17 00:00:00 2001 From: Jan Kolarik Date: Mon, 27 Nov 2023 11:56:38 +0100 Subject: [PATCH 1/4] repo: Add method for cloning root metadata --- include/libdnf5/repo/repo.hpp | 5 +++ libdnf5/repo/repo.cpp | 65 +++++++++++++++++++++++++++++++++++ 2 files changed, 70 insertions(+) diff --git a/include/libdnf5/repo/repo.hpp b/include/libdnf5/repo/repo.hpp index 2d5d8755a..0375ffc11 100644 --- a/include/libdnf5/repo/repo.hpp +++ b/include/libdnf5/repo/repo.hpp @@ -382,6 +382,11 @@ class Repo { /// Depending on the result, the repository may be marked as expired. void recompute_expired(); + /// @brief Clones repodata and solv files from the root cache. The original user repository cache is deleted. + /// The intended use case is for cloning the root cache when the user one is invalid or empty. + /// @return Whether at least the repodata cache cloning was successful. + bool clone_root_metadata(); + libdnf5::BaseWeakPtr base; ConfigRepo config; diff --git a/libdnf5/repo/repo.cpp b/libdnf5/repo/repo.cpp index f1c4543cd..0d6cce9f5 100644 --- a/libdnf5/repo/repo.cpp +++ b/libdnf5/repo/repo.cpp @@ -519,6 +519,71 @@ void Repo::internalize() { } +bool Repo::clone_root_metadata() { + auto & logger = *base->get_logger(); + + auto repo_cachedir = config.get_cachedir(); + auto base_cachedir = config.get_basecachedir_option().get_value(); + + auto base_path_pos = repo_cachedir.find(base_cachedir); + libdnf_assert( + base_path_pos != std::string::npos, + "Repo cachedir \"{}\" doesn't contain base cachedir \"{}\"", + repo_cachedir, + base_cachedir); + + auto root_repo_cachedir = repo_cachedir; + root_repo_cachedir.replace( + base_path_pos, base_cachedir.size(), config.get_main_config().get_system_cachedir_option().get_value()); + + auto root_repodata_cachedir = std::filesystem::path(root_repo_cachedir) / CACHE_METADATA_DIR; + try { + if (!std::filesystem::exists(root_repodata_cachedir)) { + return false; + } + } catch (const std::filesystem::filesystem_error & e) { + logger.debug("Error when checking root repodata at \"{}\" : \"{}\"", root_repodata_cachedir.c_str(), e.what()); + return false; + } + + auto repo_cache = RepoCache(base, repo_cachedir); + repo_cache.remove_metadata(); + repo_cache.remove_solv_files(); + + auto repodata_cachedir = std::filesystem::path(repo_cachedir) / CACHE_METADATA_DIR; + try { + std::filesystem::create_directories(repodata_cachedir); + std::filesystem::copy(root_repodata_cachedir, repodata_cachedir); + } catch (const std::filesystem::filesystem_error & e) { + logger.debug( + "Error when cloning root repodata from \"{}\" to \"{}\" : \"{}\"", + root_repodata_cachedir.c_str(), + repodata_cachedir.c_str(), + e.what()); + repo_cache.remove_metadata(); + return false; + } + + auto root_solv_cachedir = std::filesystem::path(root_repo_cachedir) / CACHE_SOLV_FILES_DIR; + auto solv_cachedir = std::filesystem::path(repo_cachedir) / CACHE_SOLV_FILES_DIR; + try { + if (std::filesystem::exists(root_solv_cachedir)) { + std::filesystem::create_directories(solv_cachedir); + std::filesystem::copy(root_solv_cachedir, solv_cachedir); + } + } catch (const std::filesystem::filesystem_error & e) { + logger.debug( + "Error when cloning root solv data from \"{}\" to \"{}\" : \"{}\"", + root_solv_cachedir.c_str(), + solv_cachedir.c_str(), + e.what()); + repo_cache.remove_solv_files(); + } + + return true; +} + + void Repo::recompute_expired() { if (expired) { return; From 6a638eb9efb35b87ee61a8d1e3c06c835382bda7 Mon Sep 17 00:00:00 2001 From: Jan Kolarik Date: Mon, 27 Nov 2023 15:07:39 +0100 Subject: [PATCH 2/4] utils: Move am_i_root function to common library --- dnf5/utils.cpp => common/utils/auth.cpp | 7 +++---- dnf5/utils.hpp => common/utils/auth.hpp | 12 ++++-------- dnf5/context.cpp | 1 - dnf5/main.cpp | 1 - 4 files changed, 7 insertions(+), 14 deletions(-) rename dnf5/utils.cpp => common/utils/auth.cpp (89%) rename dnf5/utils.hpp => common/utils/auth.hpp (83%) diff --git a/dnf5/utils.cpp b/common/utils/auth.cpp similarity index 89% rename from dnf5/utils.cpp rename to common/utils/auth.cpp index 63579a296..923813d56 100644 --- a/dnf5/utils.cpp +++ b/common/utils/auth.cpp @@ -17,15 +17,14 @@ You should have received a copy of the GNU General Public License along with libdnf. If not, see . */ -#include "utils.hpp" +#include "auth.hpp" -#include #include -namespace dnf5 { +namespace libdnf5::utils { bool am_i_root() noexcept { return geteuid() == 0; } -} // namespace dnf5 +} // namespace libdnf5::utils diff --git a/dnf5/utils.hpp b/common/utils/auth.hpp similarity index 83% rename from dnf5/utils.hpp rename to common/utils/auth.hpp index c10856ee3..3c090f737 100644 --- a/dnf5/utils.hpp +++ b/common/utils/auth.hpp @@ -17,19 +17,15 @@ You should have received a copy of the GNU General Public License along with libdnf. If not, see . */ -#ifndef DNF5_UTILS_HPP -#define DNF5_UTILS_HPP +#ifndef LIBDNF5_UTILS_AUTH_HPP +#define LIBDNF5_UTILS_AUTH_HPP -#include -#include -#include - -namespace dnf5 { +namespace libdnf5::utils { /// Returns "true" if program runs with effective user ID = 0 bool am_i_root() noexcept; -} // namespace dnf5 +} // namespace libdnf5::utils #endif diff --git a/dnf5/context.cpp b/dnf5/context.cpp index 4c9969b2c..8d65f41b3 100644 --- a/dnf5/context.cpp +++ b/dnf5/context.cpp @@ -21,7 +21,6 @@ along with libdnf. If not, see . #include "download_callbacks.hpp" #include "plugins.hpp" -#include "utils.hpp" #include "utils/url.hpp" #include diff --git a/dnf5/main.cpp b/dnf5/main.cpp index 01b7b6fe0..d61eb35a6 100644 --- a/dnf5/main.cpp +++ b/dnf5/main.cpp @@ -47,7 +47,6 @@ along with libdnf. If not, see . #include "dnf5/context.hpp" #include "download_callbacks.hpp" #include "plugins.hpp" -#include "utils.hpp" #include #include From f54f417ca97d6ef8c55ebba9c13aaf4a19a1737c Mon Sep 17 00:00:00 2001 From: Jan Kolarik Date: Mon, 27 Nov 2023 15:57:07 +0100 Subject: [PATCH 3/4] solv_repo: Add read permissions to cached solv files In order to reuse cached `*.solv` and `*.solvx` files by users, we need to add read permissions. --- libdnf5/repo/solv_repo.cpp | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/libdnf5/repo/solv_repo.cpp b/libdnf5/repo/solv_repo.cpp index 75fb9cc34..3506cf11a 100644 --- a/libdnf5/repo/solv_repo.cpp +++ b/libdnf5/repo/solv_repo.cpp @@ -585,6 +585,10 @@ void SolvRepo::write_main(bool load_after_write) { } } + std::filesystem::permissions( + cache_tmp_file.get_path(), + std::filesystem::perms::group_read | std::filesystem::perms::others_read, + std::filesystem::perm_options::add); std::filesystem::rename(cache_tmp_file.get_path(), solvfile_path); cache_tmp_file.release(); } @@ -668,6 +672,10 @@ void SolvRepo::write_ext(Id repodata_id, RepodataType type) { data->state = REPODATA_AVAILABLE; } + std::filesystem::permissions( + cache_tmp_file.get_path(), + std::filesystem::perms::group_read | std::filesystem::perms::others_read, + std::filesystem::perm_options::add); std::filesystem::rename(cache_tmp_file.get_path(), solvfile_path); cache_tmp_file.release(); } From 07b034e319bb9a714fc6aea1a49305c6e11bc15a Mon Sep 17 00:00:00 2001 From: Jan Kolarik Date: Mon, 27 Nov 2023 11:58:12 +0100 Subject: [PATCH 4/4] repo_sack: Clone root metadata when user has not valid cache --- libdnf5/repo/repo_sack.cpp | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/libdnf5/repo/repo_sack.cpp b/libdnf5/repo/repo_sack.cpp index 6f364f9b9..a62847d6b 100644 --- a/libdnf5/repo/repo_sack.cpp +++ b/libdnf5/repo/repo_sack.cpp @@ -26,6 +26,7 @@ along with libdnf. If not, see . #include "rpm/package_sack_impl.hpp" #include "solv/solver.hpp" #include "solv_repo.hpp" +#include "utils/auth.hpp" #include "utils/fs/utils.hpp" #include "utils/string.hpp" #include "utils/url.hpp" @@ -377,9 +378,15 @@ void RepoSack::update_and_load_repos(libdnf5::repo::RepoQuery & repos, bool impo repos_for_processing = std::move(repos_with_bad_signature); } + std::string prev_repo_id; + bool root_cache_tried = false; // Prepares repositories that are not expired or have ONLY_CACHE or LAZY SynStrategy. for (std::size_t idx = 0; idx < repos_for_processing.size();) { auto * const repo = repos_for_processing[idx]; + if (prev_repo_id != repo->get_id()) { + prev_repo_id = repo->get_id(); + root_cache_tried = false; + } catch_thread_sack_loader_exceptions(); try { bool valid_metadata{false}; @@ -399,6 +406,12 @@ void RepoSack::update_and_load_repos(libdnf5::repo::RepoQuery & repos, bool impo logger->debug("Using cache for repo \"{}\"", repo->config.get_id()); send_to_sack_loader(repo); } else { + // Try reusing the root cache + if (!root_cache_tried && !libdnf5::utils::am_i_root() && repo->clone_root_metadata()) { + root_cache_tried = true; + continue; + } + if (repo->get_sync_strategy() == Repo::SyncStrategy::ONLY_CACHE) { throw RepoDownloadError( M_("Cache-only enabled but no cache for repository \"{}\""), repo->config.get_id());