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