|  | // dirsearch.cc -- directory searching for gold | 
|  |  | 
|  | // Copyright (C) 2006-2021 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() ATTRIBUTE_UNUSED; | 
|  |  | 
|  | // 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. |