// dirsearch.cc -- directory searching for gold // Copyright 2006, 2007, 2008, 2009, 2010, 2011 Free Software Foundation, Inc. // Written by Ian Lance Taylor <iant@google.com>. // This file is part of gold. // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 3 of the License, or // (at your option) any later version. // This program 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., 51 Franklin Street - Fifth Floor, Boston, // MA 02110-1301, USA. #include "gold.h" #include <cerrno> #include <cstring> #include <sys/types.h> #include <sys/stat.h> #include <dirent.h> #include "debug.h" #include "gold-threads.h" #include "options.h" #include "workqueue.h" #include "dirsearch.h" namespace { // Read all the files in a directory. class Dir_cache { public: Dir_cache(const char* dirname) : dirname_(dirname), files_() { } // Read the files in the directory. void read_files(); // Return whether a file (a base name) is present in the directory. bool find(const std::string&) const; private: // We can not copy this class. Dir_cache(const Dir_cache&); Dir_cache& operator=(const Dir_cache&); const char* dirname_; Unordered_set<std::string> files_; }; void Dir_cache::read_files() { DIR* d = opendir(this->dirname_); if (d == NULL) { // We ignore directories which do not exist or are actually file // names. if (errno != ENOENT && errno != ENOTDIR) gold::gold_error(_("%s: can not read directory: %s"), this->dirname_, strerror(errno)); return; } dirent* de; while ((de = readdir(d)) != NULL) this->files_.insert(std::string(de->d_name)); if (closedir(d) != 0) gold::gold_warning("%s: closedir failed: %s", this->dirname_, strerror(errno)); } bool Dir_cache::find(const std::string& basename) const { return this->files_.find(basename) != this->files_.end(); } // A mapping from directory names to caches. A lock permits // concurrent update. There is no lock for read operations--some // other mechanism must be used to prevent reads from conflicting with // writes. class Dir_caches { public: Dir_caches() : lock_(), caches_() { } ~Dir_caches(); // Add a cache for a directory. void add(const char*); // Look up a directory in the cache. This much be locked against // calls to Add. Dir_cache* lookup(const char*) const; private: // We can not copy this class. Dir_caches(const Dir_caches&); Dir_caches& operator=(const Dir_caches&); typedef Unordered_map<const char*, Dir_cache*> Cache_hash; gold::Lock lock_; Cache_hash caches_; }; Dir_caches::~Dir_caches() { for (Cache_hash::iterator p = this->caches_.begin(); p != this->caches_.end(); ++p) delete p->second; } void Dir_caches::add(const char* dirname) { { gold::Hold_lock hl(this->lock_); if (this->lookup(dirname) != NULL) return; } Dir_cache* cache = new Dir_cache(dirname); cache->read_files(); { gold::Hold_lock hl(this->lock_); std::pair<const char*, Dir_cache*> v(dirname, cache); std::pair<Cache_hash::iterator, bool> p = this->caches_.insert(v); gold_assert(p.second); } } Dir_cache* Dir_caches::lookup(const char* dirname) const { Cache_hash::const_iterator p = this->caches_.find(dirname); if (p == this->caches_.end()) return NULL; return p->second; } // The caches. Dir_caches* caches; // A Task to read the directory. class Dir_cache_task : public gold::Task { public: Dir_cache_task(const char* dir, gold::Task_token& token) : dir_(dir), token_(token) { } gold::Task_token* is_runnable(); void locks(gold::Task_locker*); void run(gold::Workqueue*); std::string get_name() const { return std::string("Dir_cache_task ") + this->dir_; } private: const char* dir_; gold::Task_token& token_; }; // We can always run the task to read the directory. gold::Task_token* Dir_cache_task::is_runnable() { return NULL; } // Return the locks to hold. We use a blocker lock to prevent file // lookups from starting until the directory contents have been read. void Dir_cache_task::locks(gold::Task_locker* tl) { tl->add(this, &this->token_); } // Run the task--read the directory contents. void Dir_cache_task::run(gold::Workqueue*) { caches->add(this->dir_); } } namespace gold { // Initialize. void Dirsearch::initialize(Workqueue* workqueue, const General_options::Dir_list* directories) { gold_assert(caches == NULL); caches = new Dir_caches; this->directories_ = directories; this->token_.add_blockers(directories->size()); for (General_options::Dir_list::const_iterator p = directories->begin(); p != directories->end(); ++p) workqueue->queue(new Dir_cache_task(p->name().c_str(), this->token_)); } // Search for a file. NOTE: we only log failed file-lookup attempts // here. Successfully lookups will eventually get logged in // File_read::open. std::string Dirsearch::find(const std::vector<std::string>& names, bool* is_in_sysroot, int* pindex, std::string *found_name) const { gold_assert(!this->token_.is_blocked()); gold_assert(*pindex >= 0); for (unsigned int i = static_cast<unsigned int>(*pindex); i < this->directories_->size(); ++i) { const Search_directory* p = &this->directories_->at(i); Dir_cache* pdc = caches->lookup(p->name().c_str()); gold_assert(pdc != NULL); for (std::vector<std::string>::const_iterator n = names.begin(); n != names.end(); ++n) { if (pdc->find(*n)) { *is_in_sysroot = p->is_in_sysroot(); *pindex = i; *found_name = *n; return p->name() + '/' + *n; } else gold_debug(DEBUG_FILES, "Attempt to open %s/%s failed", p->name().c_str(), (*n).c_str()); } } *pindex = -2; return std::string(); } // Search for a file in a directory list. This is a low-level function and // therefore can be used before options and parameters are set. std::string Dirsearch::find_file_in_dir_list(const std::string& name, const General_options::Dir_list& directories, const std::string& extra_search_dir) { struct stat buf; std::string extra_name = extra_search_dir + '/' + name; if (stat(extra_name.c_str(), &buf) == 0) return extra_name; for (General_options::Dir_list::const_iterator dir = directories.begin(); dir != directories.end(); ++dir) { std::string full_name = dir->name() + '/' + name; if (stat(full_name.c_str(), &buf) == 0) return full_name; } return name; } } // End namespace gold.