1 // dirsearch.cc -- directory searching for gold 2 3 // Copyright (C) 2006-2014 Free Software Foundation, Inc. 4 // Written by Ian Lance Taylor <iant (at) 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 37 namespace 38 { 39 40 // Read all the files in a directory. 41 42 class 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 64 void 65 Dir_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 87 bool 88 Dir_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 98 class Dir_caches 99 { 100 public: 101 Dir_caches() 102 : lock_(), caches_() 103 { } 104 105 ~Dir_caches(); 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 125 Dir_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 133 void 134 Dir_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 155 Dir_cache* 156 Dir_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 166 Dir_caches* caches; 167 168 // A Task to read the directory. 169 170 class 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 197 gold::Task_token* 198 Dir_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 206 void 207 Dir_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 214 void 215 Dir_cache_task::run(gold::Workqueue*) 216 { 217 caches->add(this->dir_); 218 } 219 220 } 221 222 namespace gold 223 { 224 225 // Initialize. 226 227 void 228 Dirsearch::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 245 std::string 246 Dirsearch::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 284 std::string 285 Dirsearch::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