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 #include <unordered_map>
8
9 #if defined(OS_WIN)
10 #include <windows.h>
11 #include <shellapi.h>
12 #include <shlobj.h>
13 #endif
14
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"
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_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
35 // Mac and Android.
36 bool PathProviderPosix(int key, FilePath* result);
37 #endif
38
39 namespace {
40
41 typedef std::unordered_map<int, FilePath> PathMap;
42
43 // We keep a linked list of providers. In a debug build we ensure that no two
44 // providers claim overlapping keys.
45 struct Provider {
46 PathService::ProviderFunc func;
47 struct Provider* next;
48 #ifndef NDEBUG
49 int key_start;
50 int key_end;
51 #endif
52 bool is_static;
53 };
54
55 Provider base_provider = {PathProvider, nullptr,
56 #ifndef NDEBUG
57 PATH_START, PATH_END,
58 #endif
59 true};
60
61 #if defined(OS_WIN)
62 Provider base_provider_win = {
63 PathProviderWin,
64 &base_provider,
65 #ifndef NDEBUG
66 PATH_WIN_START,
67 PATH_WIN_END,
68 #endif
69 true
70 };
71 #endif
72
73 #if defined(OS_MACOSX)
74 Provider base_provider_mac = {
75 PathProviderMac,
76 &base_provider,
77 #ifndef NDEBUG
78 PATH_MAC_START,
79 PATH_MAC_END,
80 #endif
81 true
82 };
83 #endif
84
85 #if defined(OS_ANDROID)
86 Provider base_provider_android = {
87 PathProviderAndroid,
88 &base_provider,
89 #ifndef NDEBUG
90 PATH_ANDROID_START,
91 PATH_ANDROID_END,
92 #endif
93 true
94 };
95 #endif
96
97 #if defined(OS_FUCHSIA)
98 Provider base_provider_fuchsia = {PathProviderFuchsia, &base_provider,
99 #ifndef NDEBUG
100 0, 0,
101 #endif
102 true};
103 #endif
104
105 #if defined(OS_POSIX) && !defined(OS_MACOSX) && !defined(OS_ANDROID) && \
106 !defined(OS_FUCHSIA)
107 Provider base_provider_posix = {
108 PathProviderPosix,
109 &base_provider,
110 #ifndef NDEBUG
111 PATH_POSIX_START,
112 PATH_POSIX_END,
113 #endif
114 true
115 };
116 #endif
117
118
119 struct PathData {
120 Lock lock;
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;
125
PathDatabase::__anon62b218bb0111::PathData126 PathData() : cache_disabled(false) {
127 #if defined(OS_WIN)
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;
137 #endif
138 }
139 };
140
GetPathData()141 static PathData* GetPathData() {
142 static auto* path_data = new PathData();
143 return path_data;
144 }
145
146 // Tries to find |key| in the cache. |path_data| should be locked by the caller!
LockedGetFromCache(int key,const PathData * path_data,FilePath * result)147 bool LockedGetFromCache(int key, const PathData* path_data, FilePath* result) {
148 if (path_data->cache_disabled)
149 return false;
150 // check for a cached version
151 PathMap::const_iterator it = path_data->cache.find(key);
152 if (it != path_data->cache.end()) {
153 *result = it->second;
154 return true;
155 }
156 return false;
157 }
158
159 // Tries to find |key| in the overrides map. |path_data| should be locked by the
160 // caller!
LockedGetFromOverrides(int key,PathData * path_data,FilePath * result)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;
168 return true;
169 }
170 return false;
171 }
172
173 } // namespace
174
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.
178 // static
Get(int key,FilePath * result)179 bool PathService::Get(int key, FilePath* result) {
180 PathData* path_data = GetPathData();
181 DCHECK(path_data);
182 DCHECK(result);
183 DCHECK_GE(key, DIR_CURRENT);
184
185 // special case the current directory because it can never be cached
186 if (key == DIR_CURRENT)
187 return GetCurrentDirectory(result);
188
189 Provider* provider = nullptr;
190 {
191 AutoLock scoped_lock(path_data->lock);
192 if (LockedGetFromCache(key, path_data, result))
193 return true;
194
195 if (LockedGetFromOverrides(key, path_data, result))
196 return true;
197
198 // Get the beginning of the list while it is still locked.
199 provider = path_data->providers;
200 }
201
202 FilePath path;
203
204 // Iterating does not need the lock because only the list head might be
205 // modified on another thread.
206 while (provider) {
207 if (provider->func(key, &path))
208 break;
209 DCHECK(path.empty()) << "provider should not have modified path";
210 provider = provider->next;
211 }
212
213 if (path.empty())
214 return false;
215
216 if (path.ReferencesParent()) {
217 // Make sure path service never returns a path with ".." in it.
218 path = MakeAbsoluteFilePath(path);
219 if (path.empty())
220 return false;
221 }
222 *result = path;
223
224 AutoLock scoped_lock(path_data->lock);
225 if (!path_data->cache_disabled)
226 path_data->cache[key] = path;
227
228 return true;
229 }
230
231 // static
Override(int key,const FilePath & 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);
236 }
237
238 // static
OverrideAndCreateIfNeeded(int key,const FilePath & path,bool is_absolute,bool create)239 bool PathService::OverrideAndCreateIfNeeded(int key,
240 const FilePath& path,
241 bool is_absolute,
242 bool create) {
243 PathData* path_data = GetPathData();
244 DCHECK(path_data);
245 DCHECK_GT(key, DIR_CURRENT) << "invalid path key";
246
247 FilePath file_path = path;
248
249 // For some locations this will fail if called from inside the sandbox there-
250 // fore we protect this call with a flag.
251 if (create) {
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))
256 return false;
257 }
258
259 // We need to have an absolute path.
260 if (!is_absolute) {
261 file_path = MakeAbsoluteFilePath(file_path);
262 if (file_path.empty())
263 return false;
264 }
265 DCHECK(file_path.IsAbsolute());
266
267 AutoLock scoped_lock(path_data->lock);
268
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();
272
273 path_data->overrides[key] = file_path;
274
275 return true;
276 }
277
278 // static
RemoveOverride(int key)279 bool PathService::RemoveOverride(int key) {
280 PathData* path_data = GetPathData();
281 DCHECK(path_data);
282
283 AutoLock scoped_lock(path_data->lock);
284
285 if (path_data->overrides.find(key) == path_data->overrides.end())
286 return false;
287
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();
291
292 path_data->overrides.erase(key);
293
294 return true;
295 }
296
297 // static
RegisterProvider(ProviderFunc func,int key_start,int key_end)298 void PathService::RegisterProvider(ProviderFunc func, int key_start,
299 int key_end) {
300 PathData* path_data = GetPathData();
301 DCHECK(path_data);
302 DCHECK_GT(key_end, key_start);
303
304 Provider* p;
305
306 p = new Provider;
307 p->is_static = false;
308 p->func = func;
309 #ifndef NDEBUG
310 p->key_start = key_start;
311 p->key_end = key_end;
312 #endif
313
314 AutoLock scoped_lock(path_data->lock);
315
316 #ifndef NDEBUG
317 Provider *iter = path_data->providers;
318 while (iter) {
319 DCHECK(key_start >= iter->key_end || key_end <= iter->key_start) <<
320 "path provider collision";
321 iter = iter->next;
322 }
323 #endif
324
325 p->next = path_data->providers;
326 path_data->providers = p;
327 }
328
329 // static
DisableCache()330 void PathService::DisableCache() {
331 PathData* path_data = GetPathData();
332 DCHECK(path_data);
333
334 AutoLock scoped_lock(path_data->lock);
335 path_data->cache.clear();
336 path_data->cache_disabled = true;
337 }
338
339 } // namespace base
340