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 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; 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()); 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(); }