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