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