| 1 | // dirsearch.cc -- directory searching for gold |
| 2 | |
| 3 | #include "gold.h" |
| 4 | |
| 5 | #include <cerrno> |
| 6 | #include <sys/types.h> |
| 7 | #include <dirent.h> |
| 8 | |
| 9 | #include "gold-threads.h" |
| 10 | #include "dirsearch.h" |
| 11 | |
| 12 | namespace |
| 13 | { |
| 14 | |
| 15 | // Read all the files in a directory. |
| 16 | |
| 17 | class Dir_cache |
| 18 | { |
| 19 | public: |
| 20 | Dir_cache(const char* dirname) |
| 21 | : dirname_(dirname), files_() |
| 22 | { } |
| 23 | |
| 24 | // Read the files in the directory. |
| 25 | void read_files(); |
| 26 | |
| 27 | // Return whether a file (a base name) is present in the directory. |
| 28 | bool find(const std::string&) const; |
| 29 | |
| 30 | private: |
| 31 | // We can not copy this class. |
| 32 | Dir_cache(const Dir_cache&); |
| 33 | Dir_cache& operator=(const Dir_cache&); |
| 34 | |
| 35 | const char* dirname_; |
| 36 | Unordered_set<std::string> files_; |
| 37 | }; |
| 38 | |
| 39 | void |
| 40 | Dir_cache::read_files() |
| 41 | { |
| 42 | DIR* d = opendir(this->dirname_); |
| 43 | if (d == NULL) |
| 44 | { |
| 45 | // We ignore directories which do not exist. |
| 46 | if (errno == ENOENT) |
| 47 | return; |
| 48 | |
| 49 | char *s = NULL; |
| 50 | if (asprintf(&s, _("can not read directory %s"), this->dirname_) < 0) |
| 51 | gold::gold_nomem(); |
| 52 | gold::gold_fatal(s, true); |
| 53 | } |
| 54 | |
| 55 | dirent* de; |
| 56 | while ((de = readdir(d)) != NULL) |
| 57 | this->files_.insert(std::string(de->d_name)); |
| 58 | |
| 59 | if (closedir(d) != 0) |
| 60 | gold::gold_fatal("closedir failed", true); |
| 61 | } |
| 62 | |
| 63 | bool |
| 64 | Dir_cache::find(const std::string& basename) const |
| 65 | { |
| 66 | return this->files_.find(basename) != this->files_.end(); |
| 67 | } |
| 68 | |
| 69 | // A mapping from directory names to caches. A lock permits |
| 70 | // concurrent update. There is no lock for read operations--some |
| 71 | // other mechanism must be used to prevent reads from conflicting with |
| 72 | // writes. |
| 73 | |
| 74 | class Dir_caches |
| 75 | { |
| 76 | public: |
| 77 | Dir_caches() |
| 78 | : lock_(), caches_() |
| 79 | { } |
| 80 | |
| 81 | ~Dir_caches(); |
| 82 | |
| 83 | // Add a cache for a directory. |
| 84 | void add(const char*); |
| 85 | |
| 86 | // Look up a directory in the cache. This much be locked against |
| 87 | // calls to Add. |
| 88 | Dir_cache* lookup(const char*) const; |
| 89 | |
| 90 | private: |
| 91 | // We can not copy this class. |
| 92 | Dir_caches(const Dir_caches&); |
| 93 | Dir_caches& operator=(const Dir_caches&); |
| 94 | |
| 95 | typedef Unordered_map<const char*, Dir_cache*> Cache_hash; |
| 96 | |
| 97 | gold::Lock lock_; |
| 98 | Cache_hash caches_; |
| 99 | }; |
| 100 | |
| 101 | Dir_caches::~Dir_caches() |
| 102 | { |
| 103 | for (Cache_hash::iterator p = this->caches_.begin(); |
| 104 | p != this->caches_.end(); |
| 105 | ++p) |
| 106 | delete p->second; |
| 107 | } |
| 108 | |
| 109 | void |
| 110 | Dir_caches::add(const char* dirname) |
| 111 | { |
| 112 | { |
| 113 | gold::Hold_lock hl(this->lock_); |
| 114 | if (this->lookup(dirname) != NULL) |
| 115 | return; |
| 116 | } |
| 117 | |
| 118 | Dir_cache* cache = new Dir_cache(dirname); |
| 119 | |
| 120 | cache->read_files(); |
| 121 | |
| 122 | { |
| 123 | gold::Hold_lock hl(this->lock_); |
| 124 | |
| 125 | std::pair<const char*, Dir_cache*> v(dirname, cache); |
| 126 | std::pair<Cache_hash::iterator, bool> p = this->caches_.insert(v); |
| 127 | gold_assert(p.second); |
| 128 | } |
| 129 | } |
| 130 | |
| 131 | Dir_cache* |
| 132 | Dir_caches::lookup(const char* dirname) const |
| 133 | { |
| 134 | Cache_hash::const_iterator p = this->caches_.find(dirname); |
| 135 | if (p == this->caches_.end()) |
| 136 | return NULL; |
| 137 | return p->second; |
| 138 | } |
| 139 | |
| 140 | // The caches. |
| 141 | |
| 142 | Dir_caches caches; |
| 143 | |
| 144 | // A Task to read the directory. |
| 145 | |
| 146 | class Dir_cache_task : public gold::Task |
| 147 | { |
| 148 | public: |
| 149 | Dir_cache_task(const char* dir, gold::Task_token& token) |
| 150 | : dir_(dir), token_(token) |
| 151 | { } |
| 152 | |
| 153 | Is_runnable_type is_runnable(gold::Workqueue*); |
| 154 | |
| 155 | gold::Task_locker* locks(gold::Workqueue*); |
| 156 | |
| 157 | void run(gold::Workqueue*); |
| 158 | |
| 159 | private: |
| 160 | const char* dir_; |
| 161 | gold::Task_token& token_; |
| 162 | }; |
| 163 | |
| 164 | // We can always run the task to read the directory. |
| 165 | |
| 166 | gold::Task::Is_runnable_type |
| 167 | Dir_cache_task::is_runnable(gold::Workqueue*) |
| 168 | { |
| 169 | return IS_RUNNABLE; |
| 170 | } |
| 171 | |
| 172 | // Return the locks to hold. We use a blocker lock to prevent file |
| 173 | // lookups from starting until the directory contents have been read. |
| 174 | |
| 175 | gold::Task_locker* |
| 176 | Dir_cache_task::locks(gold::Workqueue* workqueue) |
| 177 | { |
| 178 | return new gold::Task_locker_block(this->token_, workqueue); |
| 179 | } |
| 180 | |
| 181 | // Run the task--read the directory contents. |
| 182 | |
| 183 | void |
| 184 | Dir_cache_task::run(gold::Workqueue*) |
| 185 | { |
| 186 | caches.add(this->dir_); |
| 187 | } |
| 188 | |
| 189 | } |
| 190 | |
| 191 | namespace gold |
| 192 | { |
| 193 | |
| 194 | Dirsearch::Dirsearch() |
| 195 | : directories_(), token_() |
| 196 | { |
| 197 | } |
| 198 | |
| 199 | void |
| 200 | Dirsearch::add(Workqueue* workqueue, const char* d) |
| 201 | { |
| 202 | this->directories_.push_back(d); |
| 203 | this->token_.add_blocker(); |
| 204 | workqueue->queue(new Dir_cache_task(d, this->token_)); |
| 205 | } |
| 206 | |
| 207 | void |
| 208 | Dirsearch::add(Workqueue* workqueue, const General_options::Dir_list& list) |
| 209 | { |
| 210 | for (General_options::Dir_list::const_iterator p = list.begin(); |
| 211 | p != list.end(); |
| 212 | ++p) |
| 213 | this->add(workqueue, *p); |
| 214 | } |
| 215 | |
| 216 | std::string |
| 217 | Dirsearch::find(const std::string& n1, const std::string& n2) const |
| 218 | { |
| 219 | gold_assert(!this->token_.is_blocked()); |
| 220 | |
| 221 | for (std::list<const char*>::const_iterator p = this->directories_.begin(); |
| 222 | p != this->directories_.end(); |
| 223 | ++p) |
| 224 | { |
| 225 | Dir_cache* pdc = caches.lookup(*p); |
| 226 | gold_assert(pdc != NULL); |
| 227 | if (pdc->find(n1)) |
| 228 | return std::string(*p) + '/' + n1; |
| 229 | if (!n2.empty() && pdc->find(n2)) |
| 230 | return std::string(*p) + '/' + n2; |
| 231 | } |
| 232 | |
| 233 | return std::string(); |
| 234 | } |
| 235 | |
| 236 | } // End namespace gold. |