diff --git a/librepomgr/buildactions/buildactionprivate.h b/librepomgr/buildactions/buildactionprivate.h index 6299b2c..bc8ac46 100644 --- a/librepomgr/buildactions/buildactionprivate.h +++ b/librepomgr/buildactions/buildactionprivate.h @@ -313,8 +313,8 @@ protected: ServiceSetup &m_setup; std::shared_ptr m_buildAction; - std::set m_sourceDbs; - std::set m_destinationDbs; + std::set m_sourceDbs; // ordering important to prevent deadlocks when acquiring locks for DBs + std::set m_destinationDbs; // ordering important to prevent deadlocks when acquiring locks for DBs bool m_fromAur = false; bool m_toAur = false; bool m_hasError = false; diff --git a/librepomgr/buildactions/repomanagement.cpp b/librepomgr/buildactions/repomanagement.cpp index e503098..5f91f6e 100644 --- a/librepomgr/buildactions/repomanagement.cpp +++ b/librepomgr/buildactions/repomanagement.cpp @@ -2,6 +2,8 @@ #include "../logging.h" +#include "../libpkg/parser/utils.h" + #include #include #include @@ -13,6 +15,7 @@ #include #include +#include using namespace std; using namespace std::literals::string_view_literals; @@ -401,6 +404,11 @@ void CheckForProblems::run() auto result = std::unordered_map>(); for (auto *const db : m_destinationDbs) { + // acquire locks + const auto archLock = m_setup.locks.acquireToRead(m_buildAction->log(), ServiceSetup::Locks::forDatabase(*db)); + const auto anyLock = m_setup.locks.acquireToRead(m_buildAction->log(), ServiceSetup::Locks::forDatabase(db->name, "any")); + const auto srcLock = m_setup.locks.acquireToRead(m_buildAction->log(), ServiceSetup::Locks::forDatabase(db->name, "src")); + // check whether files exist auto &problems = result[db->name]; try { @@ -432,6 +440,7 @@ void CheckForProblems::run() } catch (const std::filesystem::filesystem_error &e) { problems.emplace_back(RepositoryProblem{ .desc = argsToString("unable to check presence of files: ", e.what()) }); } + // check for unresolved dependencies and missing libraries checkForUnresolvedPackages: auto unresolvedPackages = db->detectUnresolvedPackages( @@ -471,7 +480,9 @@ void CleanRepository::run() return; } - // find relevant repository directories + // find relevant repository directories and acquire locks + // note: Only using a shared lock here because the cleanup isn't supposed to touch any files which actually still belong to + // the repository. enum class RepoDirType { New, ArchSpecific, @@ -483,6 +494,7 @@ void CleanRepository::run() std::vector> toArchive; // old packages not belonging to the DB anymore std::vector toDelete; // non-package junk files std::unordered_set relevantDbs; + std::variant lock; RepoDirType type = RepoDirType::New; }; std::unordered_map repoDirs; @@ -494,6 +506,8 @@ void CleanRepository::run() auto &anyDir = repoDirs[anyPath.string()]; if (anyDir.type == RepoDirType::New) { anyDir.type = RepoDirType::Any; + anyDir.lock.emplace( + m_setup.locks.acquireToRead(m_buildAction->log(), ServiceSetup::Locks::forDatabase(db.name, "any"))); anyDir.canonicalPath = std::move(anyPath); } anyDir.relevantDbs.emplace(&db); @@ -506,6 +520,8 @@ void CleanRepository::run() auto &srcDir = repoDirs[srcPath.string()]; if (srcDir.type == RepoDirType::New) { srcDir.type = RepoDirType::Src; + srcDir.lock.emplace( + m_setup.locks.acquireToRead(m_buildAction->log(), ServiceSetup::Locks::forDatabase(db.name, "src"))); srcDir.canonicalPath = std::move(srcPath); } srcDir.relevantDbs.emplace(&db); @@ -522,10 +538,20 @@ void CleanRepository::run() auto parentPath = std::filesystem::path(); try { auto archSpecificPath = std::filesystem::canonical(db->localPkgDir); + const auto dbFile = argsToString(archSpecificPath, '/', db->name + ".db"); + const auto lastModified = LibPkg::lastModified(dbFile); + if (lastModified != db->lastUpdate) { + m_messages.errors.emplace_back("The db file's last modification (" % lastModified.toString() % ") does not match the last db update (" + % db->lastUpdate.toString() + + ")."); + fatalError = true; + } auto &archSpecificDir = repoDirs[archSpecificPath.string()]; parentPath = archSpecificPath.parent_path(); if (archSpecificDir.type == RepoDirType::New) { archSpecificDir.type = RepoDirType::ArchSpecific; + archSpecificDir.lock.emplace( + m_setup.locks.acquireToRead(m_buildAction->log(), ServiceSetup::Locks::forDatabase(*db))); archSpecificDir.canonicalPath = std::move(archSpecificPath); } archSpecificDir.relevantDbs.emplace(db); @@ -559,12 +585,12 @@ void CleanRepository::run() } // find relevant databases for repo dirs discovered in "find other directories next to …" step - std::vector> otherDbs; + auto otherDbs = std::vector>(); for (auto &[dirName, dirInfo] : repoDirs) { if (dirInfo.type != RepoDirType::New) { continue; } - std::vector dbFileNames; + auto dbFilePaths = std::vector(); try { // find the database file dirInfo.canonicalPath = std::filesystem::canonical(dirName); @@ -573,20 +599,36 @@ void CleanRepository::run() continue; } if (repoItem.path().extension() == ".db") { - dbFileNames.emplace_back(repoItem.path().filename()); + dbFilePaths.emplace_back(repoItem.path()); } } - if (dbFileNames.empty()) { + if (dbFilePaths.empty()) { throw std::runtime_error("no *.db file present"); } - if (dbFileNames.size() > 1) { - throw std::runtime_error("multiple/ambigous *.db files present: " + joinStrings(dbFileNames, ", ")); + if (dbFilePaths.size() > 1) { + auto dbFileNames +#if defined(__GNUC__) && !defined(__clang__) + = dbFilePaths | std::views::transform([](const std::filesystem::path &path) { return std::string(path.filename()); }); +#else + = [&dbFilePaths] { + auto res = std::vector(); + res.reserve(dbFilePaths.size()); + for (const auto &path : dbFilePaths) { + res.emplace_back(path.filename()); + } + return res; + }(); +#endif + throw std::runtime_error( + "multiple/ambigous *.db files present: " + joinStrings(dbFileNames, ", ")); } // initialize temporary database object for the repository - auto &db - = otherDbs.emplace_back(std::make_unique(dirName, argsToString(dirInfo.canonicalPath, '/', dbFileNames.front()))); + auto &db = otherDbs.emplace_back(std::make_unique(dbFilePaths.front().stem(), dbFilePaths.front())); + db->arch = dirInfo.canonicalPath.stem(); db->loadPackages(); dirInfo.relevantDbs.emplace(db.get()); + // acquire lock for db directory + dirInfo.lock.emplace(m_setup.locks.acquireToRead(m_buildAction->log(), ServiceSetup::Locks::forDatabase(*db))); // find the "any" and "src" directory db->localPkgDir = dirInfo.canonicalPath.string(); addAnyAndSrcDir(*db); @@ -729,6 +771,7 @@ void CleanRepository::run() } m_buildAction->appendOutput(Phrases::InfoMessage, "Archived/deleted ", processesItems, " files in \"", dirName, '\"', '\n'); } + repoDirs.clear(); const auto buildLock = m_setup.building.lockToWrite(); m_buildAction->resultData = std::move(m_messages);