/* vim: set sw=4 sts=4 et foldmethod=syntax : */ /* * Copyright (c) 2006, 2007, 2008, 2009, 2010, 2011 Ciaran McCreesh * * This file is part of the Paludis package manager. Paludis is free software; * you can redistribute it and/or modify it under the terms of the GNU General * Public License version 2, as published by the Free Software Foundation. * * Paludis is distributed in the hope that it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more * details. * * You should have received a copy of the GNU General Public License along with * this program; if not, write to the Free Software Foundation, Inc., 59 Temple * Place, Suite 330, Boston, MA 02111-1307 USA */ #include "repository_name_cache.hh" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include using namespace paludis; namespace paludis { typedef std::unordered_map, Hash > NameCacheMap; template<> struct Imp { mutable Mutex mutex; mutable bool usable; mutable FSPath location; const Repository * const repo; mutable NameCacheMap name_cache_map; mutable bool checked_name_cache_map; Imp(const FSPath & l, const Repository * const r) : usable(l != FSPath("/var/empty")), location(l == FSPath("/var/empty") ? l : l / stringify(r->name())), repo(r), checked_name_cache_map(false) { } NameCacheMap::iterator find(const PackageNamePart &) const; void update(const PackageNamePart & p, NameCacheMap::iterator r); }; } NameCacheMap::iterator Imp::find(const PackageNamePart & p) const { NameCacheMap::iterator r(name_cache_map.find(p)); location = FSPath(stringify(location)); if (name_cache_map.end() == r) { r = name_cache_map.insert(std::make_pair(p, std::set())).first; if (! checked_name_cache_map) { if (location.stat().is_directory() && (location / "_VERSION_").stat().exists()) { SafeIFStream vvf(location / "_VERSION_"); std::string line; std::getline(vvf, line); if (line != "paludis-2") { Log::get_instance()->message("repository.names_cache.unsupported", ll_warning, lc_context) << "Names cache for '" << repo->name() << "' has version string '" << line << "', which is not supported. Was it generated using a different Paludis version? Perhaps you need to regenerate " "the cache using 'cave fix-cache'?"; usable = false; return name_cache_map.end(); } std::getline(vvf, line); if (line != stringify(repo->name())) { Log::get_instance()->message("repository.names_cache.different", ll_warning, lc_context) << "Names cache for '" << repo->name() << "' was generated for repository '" << line << "', so it cannot be used. You must not have multiple name caches at the same location."; usable = false; return name_cache_map.end(); } checked_name_cache_map = true; } else if ((location.dirname() / "_VERSION_").stat().exists()) { Log::get_instance()->message("repository.names_cache.old", ll_warning, lc_context) << "Names cache for '" << repo->name() << "' does not exist at '" << location << "', but a names cache exists at '" << location.dirname() << "'. This was probably generated by a Paludis version " "older than 0.18.0. The names cache now automatically appends the repository name to the " "directory. You probably want to manually remove '" << location.dirname() << "' and then regenerate the cache."; usable = false; return name_cache_map.end(); } else { Log::get_instance()->message("repository.names_cache.unversioned", ll_warning, lc_context) << "Names cache for '" << repo->name() << "' has no version information, so cannot be used. Either it was generated using " "an older Paludis version or it has not yet been generated. Perhaps you need to regenerate " "the cache using 'cave fix-cache'?"; usable = false; return name_cache_map.end(); } } FSPath ff(location / stringify(p)); if (ff.stat().exists()) { SafeIFStream f(ff); std::string line; while (std::getline(f, line)) r->second.insert(CategoryNamePart(line)); } } return r; } void Imp::update(const PackageNamePart & p, NameCacheMap::iterator r) { FSPath ff(location / stringify(p)); if (r->second.empty() && ff.stat().exists()) { try { ff.unlink(); } catch (const FSError & e) { Log::get_instance()->message("repository.names_cache.unlink_failed", ll_warning, lc_context) << "Cannot unlink '" << ff << "': " << e.message() << " (" << e.what() << ")"; } return; } try { SafeOFStream f(ff, -1, true); for (std::set::const_iterator it(r->second.begin()), it_end(r->second.end()); it_end != it; ++it) f << *it << std::endl; } catch (const SafeOFStreamError & e) { Log::get_instance()->message("repository.names_cache.write_failed", ll_warning, lc_context) << "Cannot write '" << ff << "': '" << e.message() << "' (" << e.what() << ")"; return; } } RepositoryNameCache::RepositoryNameCache( const FSPath & location, const Repository * const repo) : _imp(location, repo) { } RepositoryNameCache::~RepositoryNameCache() { } std::shared_ptr RepositoryNameCache::category_names_containing_package(const PackageNamePart & p) const { Lock l(_imp->mutex); if (! usable()) return std::shared_ptr(); Context context("When using name cache at '" + stringify(_imp->location) + "':"); std::shared_ptr result(std::make_shared()); NameCacheMap::iterator r(_imp->find(p)); if (_imp->name_cache_map.end() == r) return std::shared_ptr(); std::copy(r->second.begin(), r->second.end(), result->inserter()); return result; } void RepositoryNameCache::regenerate_cache() const { Lock l(_imp->mutex); if (_imp->location == FSPath("/var/empty")) return; Context context("When generating repository names cache at '" + stringify(_imp->location) + "':"); if (_imp->location.stat().is_directory()) for (FSIterator i(_imp->location, { fsio_inode_sort }), i_end ; i != i_end ; ++i) i->unlink(); FSPath main_cache_dir(_imp->location.dirname()); FSStat main_cache_dir_stat(main_cache_dir); if (! main_cache_dir_stat.exists()) Log::get_instance()->message("repository.names_cache.no_dir", ll_warning, lc_context) << "Names cache directory '" << main_cache_dir << "' does not exist " << "(see the faq for why this directory will not be created automatically)"; if (_imp->location.mkdir(main_cache_dir_stat.permissions(), { fspmkdo_ok_if_exists })) _imp->location.chmod(main_cache_dir_stat.permissions()); std::unordered_map > m; std::shared_ptr cats(_imp->repo->category_names({ })); for (CategoryNamePartSet::ConstIterator c(cats->begin()), c_end(cats->end()) ; c != c_end ; ++c) { std::shared_ptr pkgs(_imp->repo->package_names(*c, { })); for (QualifiedPackageNameSet::ConstIterator p(pkgs->begin()), p_end(pkgs->end()) ; p != p_end ; ++p) m[stringify(p->package())].append(stringify(*c) + "\n"); } for (std::unordered_map >::const_iterator e(m.begin()), e_end(m.end()) ; e != e_end ; ++e) { try { SafeOFStream f(_imp->location / stringify(e->first), -1, true); f << e->second; } catch (const SafeOFStreamError & ee) { Log::get_instance()->message("repository.names_cache.write_failed", ll_warning, lc_context) << "Cannot write to '" << _imp->location << "': '" << ee.message() << "' (" << ee.what() << ")"; continue; } } try { SafeOFStream f(_imp->location / "_VERSION_", -1, true);; f << "paludis-2" << std::endl; f << _imp->repo->name() << std::endl; } catch (const SafeOFStreamError & e) { Log::get_instance()->message("repository.names_cache.write_failed", ll_warning, lc_context) << "Cannot write to '" << _imp->location << "': '" << e.message() << "' (" << e.what() << ")"; } } void RepositoryNameCache::add(const QualifiedPackageName & q) { Lock l(_imp->mutex); if (! usable()) return; Context context("When adding '" + stringify(q) + "' to name cache at '" + stringify(_imp->location) + "':"); std::shared_ptr result(std::make_shared()); NameCacheMap::iterator r(_imp->find(q.package())); if (_imp->name_cache_map.end() == r) return; r->second.insert(q.category()); _imp->update(q.package(), r); } void RepositoryNameCache::remove(const QualifiedPackageName & q) { Lock l(_imp->mutex); if (! usable()) return; Context context("When removing '" + stringify(q) + "' from name cache at '" + stringify(_imp->location) + "':"); std::shared_ptr result(std::make_shared()); NameCacheMap::iterator r(_imp->find(q.package())); if (_imp->name_cache_map.end() == r) return; r->second.erase(q.category()); _imp->update(q.package(), r); } bool RepositoryNameCache::usable() const { return _imp->usable; }