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"
13 #include "base/file_util.h"
14 #include "base/files/file_path.h"
15 #include "base/hash_tables.h"
16 #include "base/lazy_instance.h"
17 #include "base/logging.h"
18 #include "base/synchronization/lock.h"
23 bool PathProvider(int key
, FilePath
* result
);
25 bool PathProviderWin(int key
, FilePath
* result
);
26 #elif defined(OS_MACOSX)
27 bool PathProviderMac(int key
, FilePath
* result
);
28 #elif defined(OS_ANDROID)
29 bool PathProviderAndroid(int key
, FilePath
* result
);
30 #elif defined(OS_POSIX)
31 // PathProviderPosix is the default path provider on POSIX OSes other than
33 bool PathProviderPosix(int key
, FilePath
* result
);
39 typedef base::hash_map
<int, FilePath
> PathMap
;
41 // We keep a linked list of providers. In a debug build we ensure that no two
42 // providers claim overlapping keys.
44 PathService::ProviderFunc func
;
45 struct Provider
* next
;
53 Provider base_provider
= {
64 Provider base_provider_win
= {
65 base::PathProviderWin
,
75 #if defined(OS_MACOSX)
76 Provider base_provider_mac
= {
77 base::PathProviderMac
,
87 #if defined(OS_ANDROID)
88 Provider base_provider_android
= {
89 base::PathProviderAndroid
,
92 base::PATH_ANDROID_START
,
93 base::PATH_ANDROID_END
,
99 #if defined(OS_POSIX) && !defined(OS_MACOSX) && !defined(OS_ANDROID)
100 Provider base_provider_posix
= {
101 base::PathProviderPosix
,
104 base::PATH_POSIX_START
,
105 base::PATH_POSIX_END
,
114 PathMap cache
; // Cache mappings from path key to path value.
115 PathMap overrides
; // Track path overrides.
116 Provider
* providers
; // Linked list of path service providers.
117 bool cache_disabled
; // Don't use cache if true;
119 PathData() : cache_disabled(false) {
121 providers
= &base_provider_win
;
122 #elif defined(OS_MACOSX)
123 providers
= &base_provider_mac
;
124 #elif defined(OS_ANDROID)
125 providers
= &base_provider_android
;
126 #elif defined(OS_POSIX)
127 providers
= &base_provider_posix
;
132 Provider
* p
= providers
;
134 Provider
* next
= p
->next
;
142 static base::LazyInstance
<PathData
> g_path_data
= LAZY_INSTANCE_INITIALIZER
;
144 static PathData
* GetPathData() {
145 return g_path_data
.Pointer();
148 // Tries to find |key| in the cache. |path_data| should be locked by the caller!
149 bool LockedGetFromCache(int key
, const PathData
* path_data
, FilePath
* result
) {
150 if (path_data
->cache_disabled
)
152 // check for a cached version
153 PathMap::const_iterator it
= path_data
->cache
.find(key
);
154 if (it
!= path_data
->cache
.end()) {
155 *result
= it
->second
;
161 // Tries to find |key| in the overrides map. |path_data| should be locked by the
163 bool LockedGetFromOverrides(int key
, PathData
* path_data
, FilePath
* result
) {
164 // check for an overridden version.
165 PathMap::const_iterator it
= path_data
->overrides
.find(key
);
166 if (it
!= path_data
->overrides
.end()) {
167 if (!path_data
->cache_disabled
)
168 path_data
->cache
[key
] = it
->second
;
169 *result
= it
->second
;
177 // TODO(brettw): this function does not handle long paths (filename > MAX_PATH)
178 // characters). This isn't supported very well by Windows right now, so it is
179 // moot, but we should keep this in mind for the future.
181 bool PathService::Get(int key
, FilePath
* result
) {
182 PathData
* path_data
= GetPathData();
185 DCHECK_GE(key
, base::DIR_CURRENT
);
187 // special case the current directory because it can never be cached
188 if (key
== base::DIR_CURRENT
)
189 return file_util::GetCurrentDirectory(result
);
191 Provider
* provider
= NULL
;
193 base::AutoLock
scoped_lock(path_data
->lock
);
194 if (LockedGetFromCache(key
, path_data
, result
))
197 if (LockedGetFromOverrides(key
, path_data
, result
))
200 // Get the beginning of the list while it is still locked.
201 provider
= path_data
->providers
;
206 // Iterating does not need the lock because only the list head might be
207 // modified on another thread.
209 if (provider
->func(key
, &path
))
211 DCHECK(path
.empty()) << "provider should not have modified path";
212 provider
= provider
->next
;
218 if (path
.ReferencesParent()) {
219 // Make sure path service never returns a path with ".." in it.
220 if (!file_util::AbsolutePath(&path
)) {
226 base::AutoLock
scoped_lock(path_data
->lock
);
227 if (!path_data
->cache_disabled
)
228 path_data
->cache
[key
] = path
;
234 bool PathService::Override(int key
, const FilePath
& path
) {
235 // Just call the full function with true for the value of |create|.
236 return OverrideAndCreateIfNeeded(key
, path
, true);
240 bool PathService::OverrideAndCreateIfNeeded(int key
,
241 const FilePath
& path
,
243 PathData
* path_data
= GetPathData();
245 DCHECK_GT(key
, base::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, AbsolutePath fails if called
254 // on a non-existent path.
255 if (!file_util::PathExists(file_path
) &&
256 !file_util::CreateDirectory(file_path
))
260 // We need to have an absolute path, as extensions and plugins don't like
261 // relative paths, and will gladly crash the browser in CHECK()s if they get a
263 if (!file_util::AbsolutePath(&file_path
))
266 base::AutoLock
scoped_lock(path_data
->lock
);
268 // Clear the cache now. Some of its entries could have depended
269 // on the value we are overriding, and are now out of sync with reality.
270 path_data
->cache
.clear();
272 path_data
->overrides
[key
] = file_path
;
278 bool PathService::RemoveOverride(int key
) {
279 PathData
* path_data
= GetPathData();
282 base::AutoLock
scoped_lock(path_data
->lock
);
284 if (path_data
->overrides
.find(key
) == path_data
->overrides
.end())
287 // Clear the cache now. Some of its entries could have depended on the value
288 // we are going to remove, and are now out of sync.
289 path_data
->cache
.clear();
291 path_data
->overrides
.erase(key
);
297 void PathService::RegisterProvider(ProviderFunc func
, int key_start
,
299 PathData
* path_data
= GetPathData();
301 DCHECK_GT(key_end
, key_start
);
306 p
->is_static
= false;
309 p
->key_start
= key_start
;
310 p
->key_end
= key_end
;
313 base::AutoLock
scoped_lock(path_data
->lock
);
316 Provider
*iter
= path_data
->providers
;
318 DCHECK(key_start
>= iter
->key_end
|| key_end
<= iter
->key_start
) <<
319 "path provider collision";
324 p
->next
= path_data
->providers
;
325 path_data
->providers
= p
;
329 void PathService::DisableCache() {
330 PathData
* path_data
= GetPathData();
333 base::AutoLock
scoped_lock(path_data
->lock
);
334 path_data
->cache
.clear();
335 path_data
->cache_disabled
= true;