Home | History | Annotate | Download | only in gold
      1 // dirsearch.cc -- directory searching for gold
      2 
      3 // Copyright (C) 2006-2016 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() 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 
    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