blob: 1b9d3949300bb9c3286f8f365c8588a4540936fa [file] [log] [blame]
Luis Hector Chavez645501c2016-12-28 10:56:26 -08001// 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"
Luis Hector Chavez645501c2016-12-28 10:56:26 -080016#include "base/logging.h"
17#include "base/synchronization/lock.h"
18#include "build/build_config.h"
19
20namespace base {
21
22bool PathProvider(int key, FilePath* result);
23
24#if defined(OS_WIN)
25bool PathProviderWin(int key, FilePath* result);
26#elif defined(OS_MACOSX)
27bool PathProviderMac(int key, FilePath* result);
28#elif defined(OS_ANDROID)
29bool 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.
33bool PathProviderPosix(int key, FilePath* result);
34#endif
35
36namespace {
37
38typedef 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.
42struct 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
52Provider 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)
63Provider 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)
75Provider 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)
87Provider 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)
99Provider 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
111struct 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
118 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
Luis Hector Chavez645501c2016-12-28 10:56:26 -0800131static PathData* GetPathData() {
Jay Civellicfc1eaa2017-08-21 17:18:10 -0700132 static auto* path_data = new PathData();
133 return path_data;
Luis Hector Chavez645501c2016-12-28 10:56:26 -0800134}
135
136// Tries to find |key| in the cache. |path_data| should be locked by the caller!
137bool 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!
151bool 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
169bool 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
222bool 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
229bool 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
269bool 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
288void 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
320void 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