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