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/containers/hash_tables.h"
14 #include "base/file_util.h"
15 #include "base/files/file_path.h"
16 #include "base/lazy_instance.h"
17 #include "base/logging.h"
18 #include "base/synchronization/lock.h"
21 using base::MakeAbsoluteFilePath
;
24 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_POSIX)
32 // PathProviderPosix is the default path provider on POSIX OSes other than
34 bool PathProviderPosix(int key
, FilePath
* result
);
40 typedef base::hash_map
<int, FilePath
> PathMap
;
42 // We keep a linked list of providers. In a debug build we ensure that no two
43 // providers claim overlapping keys.
45 PathService::ProviderFunc func
;
46 struct Provider
* next
;
54 Provider base_provider
= {
65 Provider base_provider_win
= {
66 base::PathProviderWin
,
76 #if defined(OS_MACOSX)
77 Provider base_provider_mac
= {
78 base::PathProviderMac
,
88 #if defined(OS_ANDROID)
89 Provider base_provider_android
= {
90 base::PathProviderAndroid
,
93 base::PATH_ANDROID_START
,
94 base::PATH_ANDROID_END
,
100 #if defined(OS_POSIX) && !defined(OS_MACOSX) && !defined(OS_ANDROID)
101 Provider base_provider_posix
= {
102 base::PathProviderPosix
,
105 base::PATH_POSIX_START
,
106 base::PATH_POSIX_END
,
115 PathMap cache
; // Cache mappings from path key to path value.
116 PathMap overrides
; // Track path overrides.
117 Provider
* providers
; // Linked list of path service providers.
118 bool cache_disabled
; // Don't use cache if true;
120 PathData() : cache_disabled(false) {
122 providers
= &base_provider_win
;
123 #elif defined(OS_MACOSX)
124 providers
= &base_provider_mac
;
125 #elif defined(OS_ANDROID)
126 providers
= &base_provider_android
;
127 #elif defined(OS_POSIX)
128 providers
= &base_provider_posix
;
133 Provider
* p
= providers
;
135 Provider
* next
= p
->next
;
143 static base::LazyInstance
<PathData
> g_path_data
= LAZY_INSTANCE_INITIALIZER
;
145 static PathData
* GetPathData() {
146 return g_path_data
.Pointer();
149 // Tries to find |key| in the cache. |path_data| should be locked by the caller!
150 bool LockedGetFromCache(int key
, const PathData
* path_data
, FilePath
* result
) {
151 if (path_data
->cache_disabled
)
153 // check for a cached version
154 PathMap::const_iterator it
= path_data
->cache
.find(key
);
155 if (it
!= path_data
->cache
.end()) {
156 *result
= it
->second
;
162 // Tries to find |key| in the overrides map. |path_data| should be locked by the
164 bool LockedGetFromOverrides(int key
, PathData
* path_data
, FilePath
* result
) {
165 // check for an overridden version.
166 PathMap::const_iterator it
= path_data
->overrides
.find(key
);
167 if (it
!= path_data
->overrides
.end()) {
168 if (!path_data
->cache_disabled
)
169 path_data
->cache
[key
] = it
->second
;
170 *result
= it
->second
;
178 // TODO(brettw): this function does not handle long paths (filename > MAX_PATH)
179 // characters). This isn't supported very well by Windows right now, so it is
180 // moot, but we should keep this in mind for the future.
182 bool PathService::Get(int key
, FilePath
* result
) {
183 PathData
* path_data
= GetPathData();
186 DCHECK_GE(key
, base::DIR_CURRENT
);
188 // special case the current directory because it can never be cached
189 if (key
== base::DIR_CURRENT
)
190 return file_util::GetCurrentDirectory(result
);
192 Provider
* provider
= NULL
;
194 base::AutoLock
scoped_lock(path_data
->lock
);
195 if (LockedGetFromCache(key
, path_data
, result
))
198 if (LockedGetFromOverrides(key
, path_data
, result
))
201 // Get the beginning of the list while it is still locked.
202 provider
= path_data
->providers
;
207 // Iterating does not need the lock because only the list head might be
208 // modified on another thread.
210 if (provider
->func(key
, &path
))
212 DCHECK(path
.empty()) << "provider should not have modified path";
213 provider
= provider
->next
;
219 if (path
.ReferencesParent()) {
220 // Make sure path service never returns a path with ".." in it.
221 path
= MakeAbsoluteFilePath(path
);
227 base::AutoLock
scoped_lock(path_data
->lock
);
228 if (!path_data
->cache_disabled
)
229 path_data
->cache
[key
] = path
;
235 bool PathService::Override(int key
, const FilePath
& path
) {
236 // Just call the full function with true for the value of |create|.
237 return OverrideAndCreateIfNeeded(key
, path
, true);
241 bool PathService::OverrideAndCreateIfNeeded(int key
,
242 const FilePath
& path
,
244 PathData
* path_data
= GetPathData();
246 DCHECK_GT(key
, base::DIR_CURRENT
) << "invalid path key";
248 FilePath file_path
= path
;
250 // For some locations this will fail if called from inside the sandbox there-
251 // fore we protect this call with a flag.
253 // Make sure the directory exists. We need to do this before we translate
254 // this to the absolute path because on POSIX, MakeAbsoluteFilePath fails
255 // if called on a non-existent path.
256 if (!file_util::PathExists(file_path
) &&
257 !file_util::CreateDirectory(file_path
))
261 // We need to have an absolute path.
262 file_path
= MakeAbsoluteFilePath(file_path
);
263 if (file_path
.empty())
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;