1// dirsearch.cc -- directory searching for gold 2 3// Copyright (C) 2006-2017 Free Software Foundation, Inc. 4// Written by Ian Lance Taylor <iant@google.com>. 5 6// This file is part of gold. 7 8// This program is free software; you can redistribute it and/or modify 9// it under the terms of the GNU General Public License as published by 10// the Free Software Foundation; either version 3 of the License, or 11// (at your option) any later version. 12 13// This program is distributed in the hope that it will be useful, 14// but WITHOUT ANY WARRANTY; without even the implied warranty of 15// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16// GNU General Public License for more details. 17 18// You should have received a copy of the GNU General Public License 19// along with this program; if not, write to the Free Software 20// Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, 21// MA 02110-1301, USA. 22 23#include "gold.h" 24 25#include <cerrno> 26#include <cstring> 27#include <sys/types.h> 28#include <sys/stat.h> 29#include <dirent.h> 30 31#include "debug.h" 32#include "gold-threads.h" 33#include "options.h" 34#include "workqueue.h" 35#include "dirsearch.h" 36 37namespace 38{ 39 40// Read all the files in a directory. 41 42class Dir_cache 43{ 44 public: 45 Dir_cache(const char* dirname) 46 : dirname_(dirname), files_() 47 { } 48 49 // Read the files in the directory. 50 void read_files(); 51 52 // Return whether a file (a base name) is present in the directory. 53 bool find(const std::string&) const; 54 55 private: 56 // We can not copy this class. 57 Dir_cache(const Dir_cache&); 58 Dir_cache& operator=(const Dir_cache&); 59 60 const char* dirname_; 61 Unordered_set<std::string> files_; 62}; 63 64void 65Dir_cache::read_files() 66{ 67 DIR* d = opendir(this->dirname_); 68 if (d == NULL) 69 { 70 // We ignore directories which do not exist or are actually file 71 // names. 72 if (errno != ENOENT && errno != ENOTDIR) 73 gold::gold_error(_("%s: can not read directory: %s"), 74 this->dirname_, strerror(errno)); 75 return; 76 } 77 78 dirent* de; 79 while ((de = readdir(d)) != NULL) 80 this->files_.insert(std::string(de->d_name)); 81 82 if (closedir(d) != 0) 83 gold::gold_warning("%s: closedir failed: %s", this->dirname_, 84 strerror(errno)); 85} 86 87bool 88Dir_cache::find(const std::string& basename) const 89{ 90 return this->files_.find(basename) != this->files_.end(); 91} 92 93// A mapping from directory names to caches. A lock permits 94// concurrent update. There is no lock for read operations--some 95// other mechanism must be used to prevent reads from conflicting with 96// writes. 97 98class Dir_caches 99{ 100 public: 101 Dir_caches() 102 : lock_(), caches_() 103 { } 104 105 ~Dir_caches() ATTRIBUTE_UNUSED; 106 107 // Add a cache for a directory. 108 void add(const char*); 109 110 // Look up a directory in the cache. This much be locked against 111 // calls to Add. 112 Dir_cache* lookup(const char*) const; 113 114 private: 115 // We can not copy this class. 116 Dir_caches(const Dir_caches&); 117 Dir_caches& operator=(const Dir_caches&); 118 119 typedef Unordered_map<const char*, Dir_cache*> Cache_hash; 120 121 gold::Lock lock_; 122 Cache_hash caches_; 123}; 124 125Dir_caches::~Dir_caches() 126{ 127 for (Cache_hash::iterator p = this->caches_.begin(); 128 p != this->caches_.end(); 129 ++p) 130 delete p->second; 131} 132 133void 134Dir_caches::add(const char* dirname) 135{ 136 { 137 gold::Hold_lock hl(this->lock_); 138 if (this->lookup(dirname) != NULL) 139 return; 140 } 141 142 Dir_cache* cache = new Dir_cache(dirname); 143 144 cache->read_files(); 145 146 { 147 gold::Hold_lock hl(this->lock_); 148 149 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