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