1*fae548d3Szrj // dirsearch.cc -- directory searching for gold
2*fae548d3Szrj 
3*fae548d3Szrj // Copyright (C) 2006-2020 Free Software Foundation, Inc.
4*fae548d3Szrj // Written by Ian Lance Taylor <iant@google.com>.
5*fae548d3Szrj 
6*fae548d3Szrj // This file is part of gold.
7*fae548d3Szrj 
8*fae548d3Szrj // This program is free software; you can redistribute it and/or modify
9*fae548d3Szrj // it under the terms of the GNU General Public License as published by
10*fae548d3Szrj // the Free Software Foundation; either version 3 of the License, or
11*fae548d3Szrj // (at your option) any later version.
12*fae548d3Szrj 
13*fae548d3Szrj // This program is distributed in the hope that it will be useful,
14*fae548d3Szrj // but WITHOUT ANY WARRANTY; without even the implied warranty of
15*fae548d3Szrj // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16*fae548d3Szrj // GNU General Public License for more details.
17*fae548d3Szrj 
18*fae548d3Szrj // You should have received a copy of the GNU General Public License
19*fae548d3Szrj // along with this program; if not, write to the Free Software
20*fae548d3Szrj // Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston,
21*fae548d3Szrj // MA 02110-1301, USA.
22*fae548d3Szrj 
23*fae548d3Szrj #include "gold.h"
24*fae548d3Szrj 
25*fae548d3Szrj #include <cerrno>
26*fae548d3Szrj #include <cstring>
27*fae548d3Szrj #include <sys/types.h>
28*fae548d3Szrj #include <sys/stat.h>
29*fae548d3Szrj #include <dirent.h>
30*fae548d3Szrj 
31*fae548d3Szrj #include "debug.h"
32*fae548d3Szrj #include "gold-threads.h"
33*fae548d3Szrj #include "options.h"
34*fae548d3Szrj #include "workqueue.h"
35*fae548d3Szrj #include "dirsearch.h"
36*fae548d3Szrj 
37*fae548d3Szrj namespace
38*fae548d3Szrj {
39*fae548d3Szrj 
40*fae548d3Szrj // Read all the files in a directory.
41*fae548d3Szrj 
42*fae548d3Szrj class Dir_cache
43*fae548d3Szrj {
44*fae548d3Szrj  public:
Dir_cache(const char * dirname)45*fae548d3Szrj   Dir_cache(const char* dirname)
46*fae548d3Szrj     : dirname_(dirname), files_()
47*fae548d3Szrj   { }
48*fae548d3Szrj 
49*fae548d3Szrj   // Read the files in the directory.
50*fae548d3Szrj   void read_files();
51*fae548d3Szrj 
52*fae548d3Szrj   // Return whether a file (a base name) is present in the directory.
53*fae548d3Szrj   bool find(const std::string&) const;
54*fae548d3Szrj 
55*fae548d3Szrj  private:
56*fae548d3Szrj   // We can not copy this class.
57*fae548d3Szrj   Dir_cache(const Dir_cache&);
58*fae548d3Szrj   Dir_cache& operator=(const Dir_cache&);
59*fae548d3Szrj 
60*fae548d3Szrj   const char* dirname_;
61*fae548d3Szrj   Unordered_set<std::string> files_;
62*fae548d3Szrj };
63*fae548d3Szrj 
64*fae548d3Szrj void
read_files()65*fae548d3Szrj Dir_cache::read_files()
66*fae548d3Szrj {
67*fae548d3Szrj   DIR* d = opendir(this->dirname_);
68*fae548d3Szrj   if (d == NULL)
69*fae548d3Szrj     {
70*fae548d3Szrj       // We ignore directories which do not exist or are actually file
71*fae548d3Szrj       // names.
72*fae548d3Szrj       if (errno != ENOENT && errno != ENOTDIR)
73*fae548d3Szrj 	gold::gold_error(_("%s: can not read directory: %s"),
74*fae548d3Szrj 			 this->dirname_, strerror(errno));
75*fae548d3Szrj       return;
76*fae548d3Szrj     }
77*fae548d3Szrj 
78*fae548d3Szrj   dirent* de;
79*fae548d3Szrj   while ((de = readdir(d)) != NULL)
80*fae548d3Szrj     this->files_.insert(std::string(de->d_name));
81*fae548d3Szrj 
82*fae548d3Szrj   if (closedir(d) != 0)
83*fae548d3Szrj     gold::gold_warning("%s: closedir failed: %s", this->dirname_,
84*fae548d3Szrj 		       strerror(errno));
85*fae548d3Szrj }
86*fae548d3Szrj 
87*fae548d3Szrj bool
find(const std::string & basename) const88*fae548d3Szrj Dir_cache::find(const std::string& basename) const
89*fae548d3Szrj {
90*fae548d3Szrj   return this->files_.find(basename) != this->files_.end();
91*fae548d3Szrj }
92*fae548d3Szrj 
93*fae548d3Szrj // A mapping from directory names to caches.  A lock permits
94*fae548d3Szrj // concurrent update.  There is no lock for read operations--some
95*fae548d3Szrj // other mechanism must be used to prevent reads from conflicting with
96*fae548d3Szrj // writes.
97*fae548d3Szrj 
98*fae548d3Szrj class Dir_caches
99*fae548d3Szrj {
100*fae548d3Szrj  public:
Dir_caches()101*fae548d3Szrj   Dir_caches()
102*fae548d3Szrj     : lock_(), caches_()
103*fae548d3Szrj   { }
104*fae548d3Szrj 
105*fae548d3Szrj   ~Dir_caches() ATTRIBUTE_UNUSED;
106*fae548d3Szrj 
107*fae548d3Szrj   // Add a cache for a directory.
108*fae548d3Szrj   void add(const char*);
109*fae548d3Szrj 
110*fae548d3Szrj   // Look up a directory in the cache.  This much be locked against
111*fae548d3Szrj   // calls to Add.
112*fae548d3Szrj   Dir_cache* lookup(const char*) const;
113*fae548d3Szrj 
114*fae548d3Szrj  private:
115*fae548d3Szrj   // We can not copy this class.
116*fae548d3Szrj   Dir_caches(const Dir_caches&);
117*fae548d3Szrj   Dir_caches& operator=(const Dir_caches&);
118*fae548d3Szrj 
119*fae548d3Szrj   typedef Unordered_map<const char*, Dir_cache*> Cache_hash;
120*fae548d3Szrj 
121*fae548d3Szrj   gold::Lock lock_;
122*fae548d3Szrj   Cache_hash caches_;
123*fae548d3Szrj };
124*fae548d3Szrj 
~Dir_caches()125*fae548d3Szrj Dir_caches::~Dir_caches()
126*fae548d3Szrj {
127*fae548d3Szrj   for (Cache_hash::iterator p = this->caches_.begin();
128*fae548d3Szrj        p != this->caches_.end();
129*fae548d3Szrj        ++p)
130*fae548d3Szrj     delete p->second;
131*fae548d3Szrj }
132*fae548d3Szrj 
133*fae548d3Szrj void
add(const char * dirname)134*fae548d3Szrj Dir_caches::add(const char* dirname)
135*fae548d3Szrj {
136*fae548d3Szrj   {
137*fae548d3Szrj     gold::Hold_lock hl(this->lock_);
138*fae548d3Szrj     if (this->lookup(dirname) != NULL)
139*fae548d3Szrj       return;
140*fae548d3Szrj   }
141*fae548d3Szrj 
142*fae548d3Szrj   Dir_cache* cache = new Dir_cache(dirname);
143*fae548d3Szrj 
144*fae548d3Szrj   cache->read_files();
145*fae548d3Szrj 
146*fae548d3Szrj   {
147*fae548d3Szrj     gold::Hold_lock hl(this->lock_);
148*fae548d3Szrj 
149*fae548d3Szrj     std::pair<const char*, Dir_cache*> v(dirname, cache);
150*fae548d3Szrj     std::pair<Cache_hash::iterator, bool> p = this->caches_.insert(v);
151*fae548d3Szrj     gold_assert(p.second);
152*fae548d3Szrj   }
153*fae548d3Szrj }
154*fae548d3Szrj 
155*fae548d3Szrj Dir_cache*
lookup(const char * dirname) const156*fae548d3Szrj Dir_caches::lookup(const char* dirname) const
157*fae548d3Szrj {
158*fae548d3Szrj   Cache_hash::const_iterator p = this->caches_.find(dirname);
159*fae548d3Szrj   if (p == this->caches_.end())
160*fae548d3Szrj     return NULL;
161*fae548d3Szrj   return p->second;
162*fae548d3Szrj }
163*fae548d3Szrj 
164*fae548d3Szrj // The caches.
165*fae548d3Szrj 
166*fae548d3Szrj Dir_caches* caches;
167*fae548d3Szrj 
168*fae548d3Szrj // A Task to read the directory.
169*fae548d3Szrj 
170*fae548d3Szrj class Dir_cache_task : public gold::Task
171*fae548d3Szrj {
172*fae548d3Szrj  public:
Dir_cache_task(const char * dir,gold::Task_token & token)173*fae548d3Szrj   Dir_cache_task(const char* dir, gold::Task_token& token)
174*fae548d3Szrj     : dir_(dir), token_(token)
175*fae548d3Szrj   { }
176*fae548d3Szrj 
177*fae548d3Szrj   gold::Task_token*
178*fae548d3Szrj   is_runnable();
179*fae548d3Szrj 
180*fae548d3Szrj   void
181*fae548d3Szrj   locks(gold::Task_locker*);
182*fae548d3Szrj 
183*fae548d3Szrj   void
184*fae548d3Szrj   run(gold::Workqueue*);
185*fae548d3Szrj 
186*fae548d3Szrj   std::string
get_name() const187*fae548d3Szrj   get_name() const
188*fae548d3Szrj   { return std::string("Dir_cache_task ") + this->dir_; }
189*fae548d3Szrj 
190*fae548d3Szrj  private:
191*fae548d3Szrj   const char* dir_;
192*fae548d3Szrj   gold::Task_token& token_;
193*fae548d3Szrj };
194*fae548d3Szrj 
195*fae548d3Szrj // We can always run the task to read the directory.
196*fae548d3Szrj 
197*fae548d3Szrj gold::Task_token*
is_runnable()198*fae548d3Szrj Dir_cache_task::is_runnable()
199*fae548d3Szrj {
200*fae548d3Szrj   return NULL;
201*fae548d3Szrj }
202*fae548d3Szrj 
203*fae548d3Szrj // Return the locks to hold.  We use a blocker lock to prevent file
204*fae548d3Szrj // lookups from starting until the directory contents have been read.
205*fae548d3Szrj 
206*fae548d3Szrj void
locks(gold::Task_locker * tl)207*fae548d3Szrj Dir_cache_task::locks(gold::Task_locker* tl)
208*fae548d3Szrj {
209*fae548d3Szrj   tl->add(this, &this->token_);
210*fae548d3Szrj }
211*fae548d3Szrj 
212*fae548d3Szrj // Run the task--read the directory contents.
213*fae548d3Szrj 
214*fae548d3Szrj void
run(gold::Workqueue *)215*fae548d3Szrj Dir_cache_task::run(gold::Workqueue*)
216*fae548d3Szrj {
217*fae548d3Szrj   caches->add(this->dir_);
218*fae548d3Szrj }
219*fae548d3Szrj 
220*fae548d3Szrj }
221*fae548d3Szrj 
222*fae548d3Szrj namespace gold
223*fae548d3Szrj {
224*fae548d3Szrj 
225*fae548d3Szrj // Initialize.
226*fae548d3Szrj 
227*fae548d3Szrj void
initialize(Workqueue * workqueue,const General_options::Dir_list * directories)228*fae548d3Szrj Dirsearch::initialize(Workqueue* workqueue,
229*fae548d3Szrj 		      const General_options::Dir_list* directories)
230*fae548d3Szrj {
231*fae548d3Szrj   gold_assert(caches == NULL);
232*fae548d3Szrj   caches = new Dir_caches;
233*fae548d3Szrj   this->directories_ = directories;
234*fae548d3Szrj   this->token_.add_blockers(directories->size());
235*fae548d3Szrj   for (General_options::Dir_list::const_iterator p = directories->begin();
236*fae548d3Szrj        p != directories->end();
237*fae548d3Szrj        ++p)
238*fae548d3Szrj     workqueue->queue(new Dir_cache_task(p->name().c_str(), this->token_));
239*fae548d3Szrj }
240*fae548d3Szrj 
241*fae548d3Szrj // Search for a file.  NOTE: we only log failed file-lookup attempts
242*fae548d3Szrj // here.  Successfully lookups will eventually get logged in
243*fae548d3Szrj // File_read::open.
244*fae548d3Szrj 
245*fae548d3Szrj std::string
find(const std::vector<std::string> & names,bool * is_in_sysroot,int * pindex,std::string * found_name) const246*fae548d3Szrj Dirsearch::find(const std::vector<std::string>& names,
247*fae548d3Szrj 		bool* is_in_sysroot, int* pindex,
248*fae548d3Szrj 		std::string *found_name) const
249*fae548d3Szrj {
250*fae548d3Szrj   gold_assert(!this->token_.is_blocked());
251*fae548d3Szrj   gold_assert(*pindex >= 0);
252*fae548d3Szrj 
253*fae548d3Szrj   for (unsigned int i = static_cast<unsigned int>(*pindex);
254*fae548d3Szrj        i < this->directories_->size();
255*fae548d3Szrj        ++i)
256*fae548d3Szrj     {
257*fae548d3Szrj       const Search_directory* p = &this->directories_->at(i);
258*fae548d3Szrj       Dir_cache* pdc = caches->lookup(p->name().c_str());
259*fae548d3Szrj       gold_assert(pdc != NULL);
260*fae548d3Szrj       for (std::vector<std::string>::const_iterator n = names.begin();
261*fae548d3Szrj 	   n != names.end();
262*fae548d3Szrj 	   ++n)
263*fae548d3Szrj 	{
264*fae548d3Szrj 	  if (pdc->find(*n))
265*fae548d3Szrj 	    {
266*fae548d3Szrj 	      *is_in_sysroot = p->is_in_sysroot();
267*fae548d3Szrj 	      *pindex = i;
268*fae548d3Szrj 	      *found_name = *n;
269*fae548d3Szrj 	      return p->name() + '/' + *n;
270*fae548d3Szrj 	    }
271*fae548d3Szrj 	  else
272*fae548d3Szrj 	    gold_debug(DEBUG_FILES, "Attempt to open %s/%s failed",
273*fae548d3Szrj 		       p->name().c_str(), (*n).c_str());
274*fae548d3Szrj 	}
275*fae548d3Szrj     }
276*fae548d3Szrj 
277*fae548d3Szrj   *pindex = -2;
278*fae548d3Szrj   return std::string();
279*fae548d3Szrj }
280*fae548d3Szrj 
281*fae548d3Szrj // Search for a file in a directory list.  This is a low-level function and
282*fae548d3Szrj // therefore can be used before options and parameters are set.
283*fae548d3Szrj 
284*fae548d3Szrj std::string
find_file_in_dir_list(const std::string & name,const General_options::Dir_list & directories,const std::string & extra_search_dir)285*fae548d3Szrj Dirsearch::find_file_in_dir_list(const std::string& name,
286*fae548d3Szrj                                  const General_options::Dir_list& directories,
287*fae548d3Szrj                                  const std::string& extra_search_dir)
288*fae548d3Szrj {
289*fae548d3Szrj   struct stat buf;
290*fae548d3Szrj   std::string extra_name = extra_search_dir + '/' + name;
291*fae548d3Szrj 
292*fae548d3Szrj   if (stat(extra_name.c_str(), &buf) == 0)
293*fae548d3Szrj     return extra_name;
294*fae548d3Szrj   for (General_options::Dir_list::const_iterator dir = directories.begin();
295*fae548d3Szrj        dir != directories.end();
296*fae548d3Szrj        ++dir)
297*fae548d3Szrj     {
298*fae548d3Szrj       std::string full_name = dir->name() + '/' + name;
299*fae548d3Szrj       if (stat(full_name.c_str(), &buf) == 0)
300*fae548d3Szrj         return full_name;
301*fae548d3Szrj     }
302*fae548d3Szrj   return name;
303*fae548d3Szrj }
304*fae548d3Szrj 
305*fae548d3Szrj } // End namespace gold.
306