1 // Copyright 2012 The Chromium Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "base/path_service.h"
6
7 #include <unordered_map>
8 #include <utility>
9
10 #include "base/check_op.h"
11 #include "base/files/file_path.h"
12 #include "base/files/file_util.h"
13 #include "base/logging.h"
14 #include "base/memory/raw_ptr.h"
15 #include "base/memory/raw_ptr_exclusion.h"
16 #include "base/synchronization/lock.h"
17 #include "build/build_config.h"
18
19 #if BUILDFLAG(IS_WIN)
20 #include <windows.h>
21
22 #include <shellapi.h>
23 #include <shlobj.h>
24 #endif
25
26 #define ENABLE_BEHAVIOUR_OVERRIDE_PROVIDER \
27 ((BUILDFLAG(IS_POSIX) && !BUILDFLAG(IS_APPLE) && !BUILDFLAG(IS_ANDROID)) || \
28 BUILDFLAG(IS_WIN))
29
30 namespace base {
31
32 // Custom behaviour providers.
33 bool EnvOverridePathProvider(int key, FilePath* result);
34
35 bool PathProvider(int key, FilePath* result);
36
37 #if BUILDFLAG(IS_WIN)
38 bool PathProviderWin(int key, FilePath* result);
39 #elif BUILDFLAG(IS_MAC)
40 bool PathProviderMac(int key, FilePath* result);
41 #elif BUILDFLAG(IS_IOS)
42 bool PathProviderIOS(int key, FilePath* result);
43 #elif BUILDFLAG(IS_ANDROID)
44 bool PathProviderAndroid(int key, FilePath* result);
45 #elif BUILDFLAG(IS_FUCHSIA)
46 bool PathProviderFuchsia(int key, FilePath* result);
47 #elif BUILDFLAG(IS_POSIX)
48 // PathProviderPosix is the default path provider on POSIX OSes other than
49 // Mac and Android.
50 bool PathProviderPosix(int key, FilePath* result);
51 #endif
52
53 namespace {
54
55 typedef std::unordered_map<int, FilePath> PathMap;
56
57 // We keep a linked list of providers. In a debug build we ensure that no two
58 // providers claim overlapping keys.
59 struct Provider {
60 PathService::ProviderFunc func;
61 // This field is not a raw_ptr<> because it was filtered by the rewriter for:
62 // #reinterpret-cast-trivial-type, #global-scope
63 RAW_PTR_EXCLUSION struct Provider* next;
64 #ifndef NDEBUG
65 int key_start;
66 int key_end;
67 #endif
68 bool is_static;
69 };
70
71 Provider base_provider = {PathProvider, nullptr,
72 #ifndef NDEBUG
73 PATH_START, PATH_END,
74 #endif
75 true};
76
77 #if BUILDFLAG(IS_WIN)
78 Provider win_provider = {PathProviderWin, &base_provider,
79 #ifndef NDEBUG
80 PATH_WIN_START, PATH_WIN_END,
81 #endif
82 true};
83 Provider base_provider_win = {EnvOverridePathProvider, &win_provider,
84 #ifndef NDEBUG
85 PATH_START, PATH_END,
86 #endif
87 true};
88 #endif
89
90 #if BUILDFLAG(IS_MAC)
91 Provider base_provider_mac = {
92 PathProviderMac,
93 &base_provider,
94 #ifndef NDEBUG
95 PATH_MAC_START,
96 PATH_MAC_END,
97 #endif
98 true
99 };
100 #endif
101
102 #if BUILDFLAG(IS_IOS)
103 Provider base_provider_ios = {
104 PathProviderIOS,
105 &base_provider,
106 #ifndef NDEBUG
107 PATH_IOS_START,
108 PATH_IOS_END,
109 #endif
110 true
111 };
112 #endif
113
114 #if BUILDFLAG(IS_ANDROID)
115 Provider base_provider_android = {
116 PathProviderAndroid,
117 &base_provider,
118 #ifndef NDEBUG
119 PATH_ANDROID_START,
120 PATH_ANDROID_END,
121 #endif
122 true
123 };
124 #endif
125
126 #if BUILDFLAG(IS_FUCHSIA)
127 Provider base_provider_fuchsia = {PathProviderFuchsia, &base_provider,
128 #ifndef NDEBUG
129 0, 0,
130 #endif
131 true};
132 #endif
133
134 #if BUILDFLAG(IS_POSIX) && !BUILDFLAG(IS_APPLE) && !BUILDFLAG(IS_ANDROID)
135 Provider posix_provider = {PathProviderPosix, &base_provider,
136 #ifndef NDEBUG
137 PATH_POSIX_START, PATH_POSIX_END,
138 #endif
139 true};
140 Provider base_provider_posix = {EnvOverridePathProvider, &posix_provider,
141 #ifndef NDEBUG
142 PATH_START, PATH_END,
143 #endif
144 true};
145 #endif
146
147
148 struct PathData {
149 Lock lock;
150 PathMap cache; // Cache mappings from path key to path value.
151 PathMap overrides; // Track path overrides.
152 raw_ptr<Provider> providers; // Linked list of path service providers.
153 bool cache_disabled; // Don't use cache if true;
154
PathDatabase::__anon7900a43c0111::PathData155 PathData() : cache_disabled(false) {
156 #if BUILDFLAG(IS_WIN)
157 providers = &base_provider_win;
158 #elif BUILDFLAG(IS_MAC)
159 providers = &base_provider_mac;
160 #elif BUILDFLAG(IS_IOS)
161 providers = &base_provider_ios;
162 #elif BUILDFLAG(IS_ANDROID)
163 providers = &base_provider_android;
164 #elif BUILDFLAG(IS_FUCHSIA)
165 providers = &base_provider_fuchsia;
166 #elif BUILDFLAG(IS_POSIX)
167 providers = &base_provider_posix;
168 #endif
169 }
170 };
171
GetPathData()172 static PathData* GetPathData() {
173 static auto* path_data = new PathData();
174 return path_data;
175 }
176
177 // Tries to find |key| in the cache.
LockedGetFromCache(int key,const PathData * path_data,FilePath * result)178 bool LockedGetFromCache(int key, const PathData* path_data, FilePath* result)
179 EXCLUSIVE_LOCKS_REQUIRED(path_data->lock) {
180 if (path_data->cache_disabled)
181 return false;
182 // check for a cached version
183 auto it = path_data->cache.find(key);
184 if (it != path_data->cache.end()) {
185 *result = it->second;
186 return true;
187 }
188 return false;
189 }
190
191 // Tries to find |key| in the overrides map.
LockedGetFromOverrides(int key,PathData * path_data,FilePath * result)192 bool LockedGetFromOverrides(int key, PathData* path_data, FilePath* result)
193 EXCLUSIVE_LOCKS_REQUIRED(path_data->lock) {
194 // check for an overridden version.
195 PathMap::const_iterator it = path_data->overrides.find(key);
196 if (it != path_data->overrides.end()) {
197 if (!path_data->cache_disabled)
198 path_data->cache[key] = it->second;
199 *result = it->second;
200 return true;
201 }
202 return false;
203 }
204
205 } // namespace
206
207 // TODO(brettw): this function does not handle long paths (filename > MAX_PATH)
208 // characters). This isn't supported very well by Windows right now, so it is
209 // moot, but we should keep this in mind for the future.
210 // static
Get(int key,FilePath * result)211 bool PathService::Get(int key, FilePath* result) {
212 PathData* path_data = GetPathData();
213 DCHECK(path_data);
214 DCHECK(result);
215 DCHECK_GT(key, PATH_START);
216
217 // Special case the current directory because it can never be cached.
218 if (key == DIR_CURRENT)
219 return GetCurrentDirectory(result);
220
221 Provider* provider = nullptr;
222 {
223 AutoLock scoped_lock(path_data->lock);
224 if (LockedGetFromCache(key, path_data, result))
225 return true;
226
227 if (LockedGetFromOverrides(key, path_data, result))
228 return true;
229
230 // Get the beginning of the list while it is still locked.
231 provider = path_data->providers;
232 }
233
234 FilePath path;
235
236 // Iterating does not need the lock because only the list head might be
237 // modified on another thread.
238 while (provider) {
239 if (provider->func(key, &path))
240 break;
241 DCHECK(path.empty()) << "provider should not have modified path";
242 provider = provider->next;
243 }
244
245 if (path.empty())
246 return false;
247
248 if (path.ReferencesParent()) {
249 // Make sure path service never returns a path with ".." in it.
250 path = MakeAbsoluteFilePath(path);
251 if (path.empty())
252 return false;
253 }
254 *result = path;
255
256 AutoLock scoped_lock(path_data->lock);
257 if (!path_data->cache_disabled)
258 path_data->cache[key] = path;
259
260 return true;
261 }
262
CheckedGet(int key)263 FilePath PathService::CheckedGet(int key) {
264 FilePath path;
265 LOG_IF(FATAL, !Get(key, &path)) << "Failed to get the path for " << key;
266 return path;
267 }
268
269 // static
Override(int key,const FilePath & path)270 bool PathService::Override(int key, const FilePath& path) {
271 // Just call the full function with true for the value of |create|, and
272 // assume that |path| may not be absolute yet.
273 return OverrideAndCreateIfNeeded(key, path, false, true);
274 }
275
276 // static
OverrideAndCreateIfNeeded(int key,const FilePath & path,bool is_absolute,bool create)277 bool PathService::OverrideAndCreateIfNeeded(int key,
278 const FilePath& path,
279 bool is_absolute,
280 bool create) {
281 PathData* path_data = GetPathData();
282 DCHECK(path_data);
283 DCHECK_GT(key, PATH_START) << "invalid path key";
284
285 FilePath file_path = path;
286
287 // Create the directory if requested by the caller. Do this before resolving
288 // `file_path` to an absolute path because on POSIX, MakeAbsoluteFilePath
289 // requires that the path exists.
290 if (create && !CreateDirectory(file_path)) {
291 return false;
292 }
293
294 // We need to have an absolute path.
295 if (!is_absolute) {
296 file_path = MakeAbsoluteFilePath(file_path);
297 if (file_path.empty())
298 return false;
299 }
300 DCHECK(file_path.IsAbsolute());
301
302 AutoLock scoped_lock(path_data->lock);
303
304 // Clear the cache now. Some of its entries could have depended
305 // on the value we are overriding, and are now out of sync with reality.
306 path_data->cache.clear();
307
308 path_data->overrides[key] = std::move(file_path);
309
310 return true;
311 }
312
313 // static
RemoveOverrideForTests(int key)314 bool PathService::RemoveOverrideForTests(int key) {
315 PathData* path_data = GetPathData();
316 DCHECK(path_data);
317
318 AutoLock scoped_lock(path_data->lock);
319
320 if (path_data->overrides.find(key) == path_data->overrides.end())
321 return false;
322
323 // Clear the cache now. Some of its entries could have depended on the value
324 // we are going to remove, and are now out of sync.
325 path_data->cache.clear();
326
327 path_data->overrides.erase(key);
328
329 return true;
330 }
331
332 // static
IsOverriddenForTesting(int key)333 bool PathService::IsOverriddenForTesting(int key) {
334 PathData* path_data = GetPathData();
335 DCHECK(path_data);
336
337 AutoLock scoped_lock(path_data->lock);
338
339 return path_data->overrides.find(key) != path_data->overrides.end();
340 }
341
342 // static
RegisterProvider(ProviderFunc func,int key_start,int key_end)343 void PathService::RegisterProvider(ProviderFunc func, int key_start,
344 int key_end) {
345 PathData* path_data = GetPathData();
346 DCHECK(path_data);
347 DCHECK_GT(key_end, key_start);
348
349 Provider* p;
350
351 p = new Provider;
352 p->is_static = false;
353 p->func = func;
354 #ifndef NDEBUG
355 p->key_start = key_start;
356 p->key_end = key_end;
357 #endif
358
359 AutoLock scoped_lock(path_data->lock);
360
361 #ifndef NDEBUG
362 Provider *iter = path_data->providers;
363 while (iter) {
364 DCHECK(key_start >= iter->key_end || key_end <= iter->key_start) <<
365 "path provider collision";
366 iter = iter->next;
367 }
368 #endif
369
370 p->next = path_data->providers;
371 path_data->providers = p;
372 }
373
374 // static
DisableCache()375 void PathService::DisableCache() {
376 PathData* path_data = GetPathData();
377 DCHECK(path_data);
378
379 AutoLock scoped_lock(path_data->lock);
380 path_data->cache.clear();
381 path_data->cache_disabled = true;
382 }
383
384 } // namespace base
385