155360Sobrien// dirsearch.cc -- directory searching for gold 255360Sobrien 355360Sobrien// Copyright (C) 2006-2017 Free Software Foundation, Inc. 455360Sobrien// Written by Ian Lance Taylor <iant@google.com>. 555360Sobrien 655360Sobrien// This file is part of gold. 755360Sobrien 855360Sobrien// This program is free software; you can redistribute it and/or modify 955360Sobrien// it under the terms of the GNU General Public License as published by 1055360Sobrien// the Free Software Foundation; either version 3 of the License, or 1155360Sobrien// (at your option) any later version. 1255360Sobrien 1355360Sobrien// This program is distributed in the hope that it will be useful, 1455360Sobrien// but WITHOUT ANY WARRANTY; without even the implied warranty of 1555360Sobrien// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 1655360Sobrien// GNU General Public License for more details. 1755360Sobrien 1855360Sobrien// You should have received a copy of the GNU General Public License 1955360Sobrien// along with this program; if not, write to the Free Software 2055360Sobrien// Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, 2155360Sobrien// MA 02110-1301, USA. 2255360Sobrien 2355360Sobrien#include "gold.h" 2455360Sobrien 2555360Sobrien#include <cerrno> 2655360Sobrien#include <cstring> 2755360Sobrien#include <sys/types.h> 2855360Sobrien#include <sys/stat.h> 2955360Sobrien#include <dirent.h> 3055360Sobrien 3155360Sobrien#include "debug.h" 3255360Sobrien#include "gold-threads.h" 3355360Sobrien#include "options.h" 3455360Sobrien#include "workqueue.h" 3555360Sobrien#include "dirsearch.h" 3655360Sobrien 3755360Sobriennamespace 3855360Sobrien{ 3955360Sobrien 4055360Sobrien// Read all the files in a directory. 4155360Sobrien 4255360Sobrienclass Dir_cache 4355360Sobrien{ 4455360Sobrien public: 4555360Sobrien Dir_cache(const char* dirname) 4655360Sobrien : dirname_(dirname), files_() 4755360Sobrien { } 4855360Sobrien 4955360Sobrien // Read the files in the directory. 5055360Sobrien void read_files(); 5153574Sobrien 5253574Sobrien // Return whether a file (a base name) is present in the directory. 5353574Sobrien bool find(const std::string&) const; 5453574Sobrien 5553574Sobrien private: 5653574Sobrien // We can not copy this class. 5753574Sobrien Dir_cache(const Dir_cache&); 5853574Sobrien Dir_cache& operator=(const Dir_cache&); 5953574Sobrien 6053574Sobrien const char* dirname_; 6153574Sobrien Unordered_set<std::string> files_; 6253574Sobrien}; 6353574Sobrien 6453574Sobrienvoid 6553574SobrienDir_cache::read_files() 6653574Sobrien{ 6753574Sobrien DIR* d = opendir(this->dirname_); 6853574Sobrien if (d == NULL) 6953574Sobrien { 7053574Sobrien // We ignore directories which do not exist or are actually file 7153574Sobrien // names. 7255360Sobrien if (errno != ENOENT && errno != ENOTDIR) 7353574Sobrien gold::gold_error(_("%s: can not read directory: %s"), 7453574Sobrien this->dirname_, strerror(errno)); 7553574Sobrien return; 7653574Sobrien } 7753574Sobrien 7853574Sobrien dirent* de; 7953574Sobrien while ((de = readdir(d)) != NULL) 8053574Sobrien this->files_.insert(std::string(de->d_name)); 8153574Sobrien 8253574Sobrien if (closedir(d) != 0) 8353574Sobrien gold::gold_warning("%s: closedir failed: %s", this->dirname_, 8453574Sobrien strerror(errno)); 8553574Sobrien} 8653574Sobrien 8753574Sobrienbool 8853574SobrienDir_cache::find(const std::string& basename) const 8953574Sobrien{ 9053574Sobrien return this->files_.find(basename) != this->files_.end(); 9153574Sobrien} 9253574Sobrien 9353574Sobrien// A mapping from directory names to caches. A lock permits 9453574Sobrien// concurrent update. There is no lock for read operations--some 9553574Sobrien// other mechanism must be used to prevent reads from conflicting with 9653574Sobrien// writes. 9753574Sobrien 9853574Sobrienclass Dir_caches 9953574Sobrien{ 10053574Sobrien public: 10153574Sobrien Dir_caches() 10253574Sobrien : lock_(), caches_() 10353574Sobrien { } 10453574Sobrien 10553574Sobrien ~Dir_caches() ATTRIBUTE_UNUSED; 10653574Sobrien 10753574Sobrien // Add a cache for a directory. 10853574Sobrien void add(const char*); 10953574Sobrien 11053574Sobrien // Look up a directory in the cache. This much be locked against 11153574Sobrien // calls to Add. 11253574Sobrien Dir_cache* lookup(const char*) const; 11353574Sobrien 11453574Sobrien private: 11594Snate // We can not copy this class. 11694Snate Dir_caches(const Dir_caches&); 11794Snate Dir_caches& operator=(const Dir_caches&); 11894Snate 11994Snate typedef Unordered_map<const char*, Dir_cache*> Cache_hash; 12094Snate 12194Snate gold::Lock lock_; 12294Snate Cache_hash caches_; 12394Snate}; 12494Snate 12594SnateDir_caches::~Dir_caches() 12694Snate{ 12794Snate for (Cache_hash::iterator p = this->caches_.begin(); 12894Snate p != this->caches_.end(); 12994Snate ++p) 13094Snate delete p->second; 13194Snate} 13294Snate 13394Snatevoid 13494SnateDir_caches::add(const char* dirname) 13594Snate{ 13694Snate { 13794Snate gold::Hold_lock hl(this->lock_); 13894Snate if (this->lookup(dirname) != NULL) 13994Snate return; 14094Snate } 14194Snate 14294Snate Dir_cache* cache = new Dir_cache(dirname); 14394Snate 14494Snate cache->read_files(); 14594Snate 14694Snate { 14794Snate gold::Hold_lock hl(this->lock_); 14894Snate 14994Snate std::pair<const char*, Dir_cache*> v(dirname, cache); 150 std::pair<Cache_hash::iterator, bool> p = this->caches_.insert(v); 151 gold_assert(p.second); 152 } 153} 154 155Dir_cache* 156Dir_caches::lookup(const char* dirname) const 157{ 158 Cache_hash::const_iterator p = this->caches_.find(dirname); 159 if (p == this->caches_.end()) 160 return NULL; 161 return p->second; 162} 163 164// The caches. 165 166Dir_caches* caches; 167 168// A Task to read the directory. 169 170class Dir_cache_task : public gold::Task 171{ 172 public: 173 Dir_cache_task(const char* dir, gold::Task_token& token) 174 : dir_(dir), token_(token) 175 { } 176 177 gold::Task_token* 178 is_runnable(); 179 180 void 181 locks(gold::Task_locker*); 182 183 void 184 run(gold::Workqueue*); 185 186 std::string 187 get_name() const 188 { return std::string("Dir_cache_task ") + this->dir_; } 189 190 private: 191 const char* dir_; 192 gold::Task_token& token_; 193}; 194 195// We can always run the task to read the directory. 196 197gold::Task_token* 198Dir_cache_task::is_runnable() 199{ 200 return NULL; 201} 202 203// Return the locks to hold. We use a blocker lock to prevent file 204// lookups from starting until the directory contents have been read. 205 206void 207Dir_cache_task::locks(gold::Task_locker* tl) 208{ 209 tl->add(this, &this->token_); 210} 211 212// Run the task--read the directory contents. 213 214void 215Dir_cache_task::run(gold::Workqueue*) 216{ 217 caches->add(this->dir_); 218} 219 220} 221 222namespace gold 223{ 224 225// Initialize. 226 227void 228Dirsearch::initialize(Workqueue* workqueue, 229 const General_options::Dir_list* directories) 230{ 231 gold_assert(caches == NULL); 232 caches = new Dir_caches; 233 this->directories_ = directories; 234 this->token_.add_blockers(directories->size()); 235 for (General_options::Dir_list::const_iterator p = directories->begin(); 236 p != directories->end(); 237 ++p) 238 workqueue->queue(new Dir_cache_task(p->name().c_str(), this->token_)); 239} 240 241// Search for a file. NOTE: we only log failed file-lookup attempts 242// here. Successfully lookups will eventually get logged in 243// File_read::open. 244 245std::string 246Dirsearch::find(const std::vector<std::string>& names, 247 bool* is_in_sysroot, int* pindex, 248 std::string *found_name) const 249{ 250 gold_assert(!this->token_.is_blocked()); 251 gold_assert(*pindex >= 0); 252 253 for (unsigned int i = static_cast<unsigned int>(*pindex); 254 i < this->directories_->size(); 255 ++i) 256 { 257 const Search_directory* p = &this->directories_->at(i); 258 Dir_cache* pdc = caches->lookup(p->name().c_str()); 259 gold_assert(pdc != NULL); 260 for (std::vector<std::string>::const_iterator n = names.begin(); 261 n != names.end(); 262 ++n) 263 { 264 if (pdc->find(*n)) 265 { 266 *is_in_sysroot = p->is_in_sysroot(); 267 *pindex = i; 268 *found_name = *n; 269 return p->name() + '/' + *n; 270 } 271 else 272 gold_debug(DEBUG_FILES, "Attempt to open %s/%s failed", 273 p->name().c_str(), (*n).c_str()); 274 } 275 } 276 277 *pindex = -2; 278 return std::string(); 279} 280 281// Search for a file in a directory list. This is a low-level function and 282// therefore can be used before options and parameters are set. 283 284std::string 285Dirsearch::find_file_in_dir_list(const std::string& name, 286 const General_options::Dir_list& directories, 287 const std::string& extra_search_dir) 288{ 289 struct stat buf; 290 std::string extra_name = extra_search_dir + '/' + name; 291 292 if (stat(extra_name.c_str(), &buf) == 0) 293 return extra_name; 294 for (General_options::Dir_list::const_iterator dir = directories.begin(); 295 dir != directories.end(); 296 ++dir) 297 { 298 std::string full_name = dir->name() + '/' + name; 299 if (stat(full_name.c_str(), &buf) == 0) 300 return full_name; 301 } 302 return name; 303} 304 305} // End namespace gold. 306