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.
5 #include "base/path_service.h"
7 #include <unordered_map>
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"
28 bool PathProvider(int key, FilePath* result);
31 bool PathProviderWin(int key, FilePath* result);
32 #elif BUILDFLAG(IS_MAC)
33 bool PathProviderMac(int key, FilePath* result);
34 #elif BUILDFLAG(IS_IOS)
35 bool PathProviderIOS(int key, FilePath* result);
36 #elif BUILDFLAG(IS_ANDROID)
37 bool PathProviderAndroid(int key, FilePath* result);
38 #elif BUILDFLAG(IS_FUCHSIA)
39 bool PathProviderFuchsia(int key, FilePath* result);
40 #elif BUILDFLAG(IS_POSIX)
41 // PathProviderPosix is the default path provider on POSIX OSes other than
43 bool PathProviderPosix(int key, FilePath* result);
48 typedef std::unordered_map<int, FilePath> PathMap;
50 // We keep a linked list of providers. In a debug build we ensure that no two
51 // providers claim overlapping keys.
53 PathService::ProviderFunc func;
54 // This field is not a raw_ptr<> because it was filtered by the rewriter for:
55 // #reinterpret-cast-trivial-type, #global-scope
56 RAW_PTR_EXCLUSION struct Provider* next;
64 Provider base_provider = {PathProvider, nullptr,
71 Provider base_provider_win = {
83 Provider base_provider_mac = {
95 Provider base_provider_ios = {
106 #if BUILDFLAG(IS_ANDROID)
107 Provider base_provider_android = {
118 #if BUILDFLAG(IS_FUCHSIA)
119 Provider base_provider_fuchsia = {PathProviderFuchsia, &base_provider,
126 #if BUILDFLAG(IS_POSIX) && !BUILDFLAG(IS_APPLE) && !BUILDFLAG(IS_ANDROID)
127 Provider base_provider_posix = {
141 PathMap cache; // Cache mappings from path key to path value.
142 PathMap overrides; // Track path overrides.
143 raw_ptr<Provider> providers; // Linked list of path service providers.
144 bool cache_disabled; // Don't use cache if true;
146 PathData() : cache_disabled(false) {
147 #if BUILDFLAG(IS_WIN)
148 providers = &base_provider_win;
149 #elif BUILDFLAG(IS_MAC)
150 providers = &base_provider_mac;
151 #elif BUILDFLAG(IS_IOS)
152 providers = &base_provider_ios;
153 #elif BUILDFLAG(IS_ANDROID)
154 providers = &base_provider_android;
155 #elif BUILDFLAG(IS_FUCHSIA)
156 providers = &base_provider_fuchsia;
157 #elif BUILDFLAG(IS_POSIX)
158 providers = &base_provider_posix;
163 static PathData* GetPathData() {
164 static auto* path_data = new PathData();
168 // Tries to find |key| in the cache.
169 bool LockedGetFromCache(int key, const PathData* path_data, FilePath* result)
170 EXCLUSIVE_LOCKS_REQUIRED(path_data->lock) {
171 if (path_data->cache_disabled)
173 // check for a cached version
174 auto it = path_data->cache.find(key);
175 if (it != path_data->cache.end()) {
176 *result = it->second;
182 // Tries to find |key| in the overrides map.
183 bool LockedGetFromOverrides(int key, PathData* path_data, FilePath* result)
184 EXCLUSIVE_LOCKS_REQUIRED(path_data->lock) {
185 // check for an overridden version.
186 PathMap::const_iterator it = path_data->overrides.find(key);
187 if (it != path_data->overrides.end()) {
188 if (!path_data->cache_disabled)
189 path_data->cache[key] = it->second;
190 *result = it->second;
198 // TODO(brettw): this function does not handle long paths (filename > MAX_PATH)
199 // characters). This isn't supported very well by Windows right now, so it is
200 // moot, but we should keep this in mind for the future.
202 bool PathService::Get(int key, FilePath* result) {
203 PathData* path_data = GetPathData();
206 DCHECK_GT(key, PATH_START);
208 // Special case the current directory because it can never be cached.
209 if (key == DIR_CURRENT)
210 return GetCurrentDirectory(result);
212 Provider* provider = nullptr;
214 AutoLock scoped_lock(path_data->lock);
215 if (LockedGetFromCache(key, path_data, result))
218 if (LockedGetFromOverrides(key, path_data, result))
221 // Get the beginning of the list while it is still locked.
222 provider = path_data->providers;
227 // Iterating does not need the lock because only the list head might be
228 // modified on another thread.
230 if (provider->func(key, &path))
232 DCHECK(path.empty()) << "provider should not have modified path";
233 provider = provider->next;
239 if (path.ReferencesParent()) {
240 // Make sure path service never returns a path with ".." in it.
241 path = MakeAbsoluteFilePath(path);
247 AutoLock scoped_lock(path_data->lock);
248 if (!path_data->cache_disabled)
249 path_data->cache[key] = path;
254 FilePath PathService::CheckedGet(int key) {
256 LOG_IF(FATAL, !Get(key, &path)) << "Failed to get the path for " << key;
261 bool PathService::Override(int key, const FilePath& path) {
262 // Just call the full function with true for the value of |create|, and
263 // assume that |path| may not be absolute yet.
264 return OverrideAndCreateIfNeeded(key, path, false, true);
268 bool PathService::OverrideAndCreateIfNeeded(int key,
269 const FilePath& path,
272 PathData* path_data = GetPathData();
274 DCHECK_GT(key, PATH_START) << "invalid path key";
276 FilePath file_path = path;
278 // Create the directory if requested by the caller. Do this before resolving
279 // `file_path` to an absolute path because on POSIX, MakeAbsoluteFilePath
280 // requires that the path exists.
282 #if BUILDFLAG(IS_EFL)
283 !PathExists(file_path) &&
285 !CreateDirectory(file_path)) {
289 // We need to have an absolute path.
291 file_path = MakeAbsoluteFilePath(file_path);
292 if (file_path.empty())
295 DCHECK(file_path.IsAbsolute());
297 AutoLock scoped_lock(path_data->lock);
299 // Clear the cache now. Some of its entries could have depended
300 // on the value we are overriding, and are now out of sync with reality.
301 path_data->cache.clear();
303 path_data->overrides[key] = std::move(file_path);
309 bool PathService::RemoveOverrideForTests(int key) {
310 PathData* path_data = GetPathData();
313 AutoLock scoped_lock(path_data->lock);
315 if (path_data->overrides.find(key) == path_data->overrides.end())
318 // Clear the cache now. Some of its entries could have depended on the value
319 // we are going to remove, and are now out of sync.
320 path_data->cache.clear();
322 path_data->overrides.erase(key);
328 bool PathService::IsOverriddenForTesting(int key) {
329 PathData* path_data = GetPathData();
332 AutoLock scoped_lock(path_data->lock);
334 return path_data->overrides.find(key) != path_data->overrides.end();
338 void PathService::RegisterProvider(ProviderFunc func, int key_start,
340 PathData* path_data = GetPathData();
342 DCHECK_GT(key_end, key_start);
347 p->is_static = false;
350 p->key_start = key_start;
351 p->key_end = key_end;
354 AutoLock scoped_lock(path_data->lock);
357 Provider *iter = path_data->providers;
359 DCHECK(key_start >= iter->key_end || key_end <= iter->key_start) <<
360 "path provider collision";
365 p->next = path_data->providers;
366 path_data->providers = p;
370 void PathService::DisableCache() {
371 PathData* path_data = GetPathData();
374 AutoLock scoped_lock(path_data->lock);
375 path_data->cache.clear();
376 path_data->cache_disabled = true;