1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
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>
15 #include "base/files/file_path.h"
16 #include "base/files/file_util.h"
17 #include "base/logging.h"
18 #include "base/synchronization/lock.h"
19 #include "build/build_config.h"
23 bool PathProvider(int key, FilePath* result);
26 bool PathProviderWin(int key, FilePath* result);
27 #elif defined(OS_MACOSX)
28 bool PathProviderMac(int key, FilePath* result);
29 #elif defined(OS_ANDROID)
30 bool PathProviderAndroid(int key, FilePath* result);
31 #elif defined(OS_FUCHSIA)
32 bool PathProviderFuchsia(int key, FilePath* result);
33 #elif defined(OS_POSIX)
34 // PathProviderPosix is the default path provider on POSIX OSes other than
36 bool PathProviderPosix(int key, FilePath* result);
41 typedef std::unordered_map<int, FilePath> PathMap;
43 // We keep a linked list of providers. In a debug build we ensure that no two
44 // providers claim overlapping keys.
46 PathService::ProviderFunc func;
47 struct Provider* next;
55 Provider base_provider = {PathProvider, nullptr,
62 Provider base_provider_win = {
73 #if defined(OS_MACOSX)
74 Provider base_provider_mac = {
85 #if defined(OS_ANDROID)
86 Provider base_provider_android = {
97 #if defined(OS_FUCHSIA)
98 Provider base_provider_fuchsia = {PathProviderFuchsia, &base_provider,
105 #if defined(OS_POSIX) && !defined(OS_MACOSX) && !defined(OS_ANDROID) && \
107 Provider base_provider_posix = {
121 PathMap cache; // Cache mappings from path key to path value.
122 PathMap overrides; // Track path overrides.
123 Provider* providers; // Linked list of path service providers.
124 bool cache_disabled; // Don't use cache if true;
126 PathData() : cache_disabled(false) {
128 providers = &base_provider_win;
129 #elif defined(OS_MACOSX)
130 providers = &base_provider_mac;
131 #elif defined(OS_ANDROID)
132 providers = &base_provider_android;
133 #elif defined(OS_FUCHSIA)
134 providers = &base_provider_fuchsia;
135 #elif defined(OS_POSIX)
136 providers = &base_provider_posix;
141 static PathData* GetPathData() {
142 static auto* path_data = new PathData();
146 // Tries to find |key| in the cache. |path_data| should be locked by the caller!
147 bool LockedGetFromCache(int key, const PathData* path_data, FilePath* result) {
148 if (path_data->cache_disabled)
150 // check for a cached version
151 auto it = path_data->cache.find(key);
152 if (it != path_data->cache.end()) {
153 *result = it->second;
159 // Tries to find |key| in the overrides map. |path_data| should be locked by the
161 bool LockedGetFromOverrides(int key, PathData* path_data, FilePath* result) {
162 // check for an overridden version.
163 PathMap::const_iterator it = path_data->overrides.find(key);
164 if (it != path_data->overrides.end()) {
165 if (!path_data->cache_disabled)
166 path_data->cache[key] = it->second;
167 *result = it->second;
175 // TODO(brettw): this function does not handle long paths (filename > MAX_PATH)
176 // characters). This isn't supported very well by Windows right now, so it is
177 // moot, but we should keep this in mind for the future.
179 bool PathService::Get(int key, FilePath* result) {
180 PathData* path_data = GetPathData();
183 DCHECK_GE(key, DIR_CURRENT);
185 // special case the current directory because it can never be cached
186 if (key == DIR_CURRENT)
187 return GetCurrentDirectory(result);
189 Provider* provider = nullptr;
191 AutoLock scoped_lock(path_data->lock);
192 if (LockedGetFromCache(key, path_data, result))
195 if (LockedGetFromOverrides(key, path_data, result))
198 // Get the beginning of the list while it is still locked.
199 provider = path_data->providers;
204 // Iterating does not need the lock because only the list head might be
205 // modified on another thread.
207 if (provider->func(key, &path))
209 DCHECK(path.empty()) << "provider should not have modified path";
210 provider = provider->next;
216 if (path.ReferencesParent()) {
217 // Make sure path service never returns a path with ".." in it.
218 path = MakeAbsoluteFilePath(path);
224 AutoLock scoped_lock(path_data->lock);
225 if (!path_data->cache_disabled)
226 path_data->cache[key] = path;
232 bool PathService::Override(int key, const FilePath& path) {
233 // Just call the full function with true for the value of |create|, and
234 // assume that |path| may not be absolute yet.
235 return OverrideAndCreateIfNeeded(key, path, false, true);
239 bool PathService::OverrideAndCreateIfNeeded(int key,
240 const FilePath& path,
243 PathData* path_data = GetPathData();
245 DCHECK_GT(key, DIR_CURRENT) << "invalid path key";
247 FilePath file_path = path;
249 // For some locations this will fail if called from inside the sandbox there-
250 // fore we protect this call with a flag.
252 // Make sure the directory exists. We need to do this before we translate
253 // this to the absolute path because on POSIX, MakeAbsoluteFilePath fails
254 // if called on a non-existent path.
255 if (!PathExists(file_path) && !CreateDirectory(file_path))
259 // We need to have an absolute path.
261 file_path = MakeAbsoluteFilePath(file_path);
262 if (file_path.empty())
265 DCHECK(file_path.IsAbsolute());
267 AutoLock scoped_lock(path_data->lock);
269 // Clear the cache now. Some of its entries could have depended
270 // on the value we are overriding, and are now out of sync with reality.
271 path_data->cache.clear();
273 path_data->overrides[key] = file_path;
279 bool PathService::RemoveOverride(int key) {
280 PathData* path_data = GetPathData();
283 AutoLock scoped_lock(path_data->lock);
285 if (path_data->overrides.find(key) == path_data->overrides.end())
288 // Clear the cache now. Some of its entries could have depended on the value
289 // we are going to remove, and are now out of sync.
290 path_data->cache.clear();
292 path_data->overrides.erase(key);
298 void PathService::RegisterProvider(ProviderFunc func, int key_start,
300 PathData* path_data = GetPathData();
302 DCHECK_GT(key_end, key_start);
307 p->is_static = false;
310 p->key_start = key_start;
311 p->key_end = key_end;
314 AutoLock scoped_lock(path_data->lock);
317 Provider *iter = path_data->providers;
319 DCHECK(key_start >= iter->key_end || key_end <= iter->key_start) <<
320 "path provider collision";
325 p->next = path_data->providers;
326 path_data->providers = p;
330 void PathService::DisableCache() {
331 PathData* path_data = GetPathData();
334 AutoLock scoped_lock(path_data->lock);
335 path_data->cache.clear();
336 path_data->cache_disabled = true;