blob: d1c9bb1045a5d4cff7ded79ebced53ce4c2daa3c [file] [log] [blame]
[email protected]cb571e752012-05-09 10:50:101// Copyright (c) 2012 The Chromium Authors. All rights reserved.
license.botbf09a502008-08-24 00:55:552// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
initial.commitd7cae122008-07-26 21:49:384
[email protected]09ad1e622008-08-07 20:23:095#include "base/path_service.h"
[email protected]355cc2742008-08-06 16:01:256
brettw1ce49f62017-04-27 19:42:327#include <unordered_map>
8
[email protected]31662202013-03-23 19:10:549#if defined(OS_WIN)
initial.commitd7cae122008-07-26 21:49:3810#include <windows.h>
11#include <shellapi.h>
12#include <shlobj.h>
[email protected]355cc2742008-08-06 16:01:2513#endif
initial.commitd7cae122008-07-26 21:49:3814
Hans Wennborgc3cffa62020-04-27 10:09:1215#include "base/check_op.h"
[email protected]57999812013-02-24 05:40:5216#include "base/files/file_path.h"
[email protected]e3177dd52014-08-13 20:22:1417#include "base/files/file_util.h"
Maksim Ivanoveeb2d0802020-07-17 21:44:0018#include "base/logging.h"
[email protected]20305ec2011-01-21 04:55:5219#include "base/synchronization/lock.h"
avi9b6f42932015-12-26 22:15:1420#include "build/build_config.h"
initial.commitd7cae122008-07-26 21:49:3821
22namespace base {
brettwf0dea132015-09-25 20:08:5423
24bool PathProvider(int key, FilePath* result);
25
[email protected]5af2edb92008-08-08 20:16:0826#if defined(OS_WIN)
brettwf0dea132015-09-25 20:08:5427bool PathProviderWin(int key, FilePath* result);
Avi Drissman5b286372020-07-28 21:59:3828#elif defined(OS_APPLE)
brettwf0dea132015-09-25 20:08:5429bool PathProviderMac(int key, FilePath* result);
[email protected]f7d69972011-06-21 22:34:5030#elif defined(OS_ANDROID)
brettwf0dea132015-09-25 20:08:5431bool PathProviderAndroid(int key, FilePath* result);
scottmg1ab7aa82017-05-25 05:22:4932#elif defined(OS_FUCHSIA)
33bool PathProviderFuchsia(int key, FilePath* result);
[email protected]5d1937bb2009-11-21 01:29:0034#elif defined(OS_POSIX)
brettwf0dea132015-09-25 20:08:5435// PathProviderPosix is the default path provider on POSIX OSes other than
36// Mac and Android.
37bool PathProviderPosix(int key, FilePath* result);
[email protected]ac510e12008-08-05 19:46:3138#endif
initial.commitd7cae122008-07-26 21:49:3839
40namespace {
41
brettw1ce49f62017-04-27 19:42:3242typedef std::unordered_map<int, FilePath> PathMap;
initial.commitd7cae122008-07-26 21:49:3843
44// We keep a linked list of providers. In a debug build we ensure that no two
45// providers claim overlapping keys.
46struct Provider {
47 PathService::ProviderFunc func;
48 struct Provider* next;
49#ifndef NDEBUG
50 int key_start;
51 int key_end;
52#endif
[email protected]173cb8a02008-08-20 15:47:3953 bool is_static;
initial.commitd7cae122008-07-26 21:49:3854};
55
Ivan Kotenkova16212a52017-11-08 12:37:3356Provider base_provider = {PathProvider, nullptr,
initial.commitd7cae122008-07-26 21:49:3857#ifndef NDEBUG
Ivan Kotenkova16212a52017-11-08 12:37:3358 PATH_START, PATH_END,
initial.commitd7cae122008-07-26 21:49:3859#endif
Ivan Kotenkova16212a52017-11-08 12:37:3360 true};
initial.commitd7cae122008-07-26 21:49:3861
[email protected]5d1937bb2009-11-21 01:29:0062#if defined(OS_WIN)
[email protected]da9ccfb2012-01-28 00:34:4063Provider base_provider_win = {
brettwf0dea132015-09-25 20:08:5464 PathProviderWin,
[email protected]ac510e12008-08-05 19:46:3165 &base_provider,
66#ifndef NDEBUG
brettwf0dea132015-09-25 20:08:5467 PATH_WIN_START,
68 PATH_WIN_END,
[email protected]ac510e12008-08-05 19:46:3169#endif
[email protected]173cb8a02008-08-20 15:47:3970 true
[email protected]ac510e12008-08-05 19:46:3171};
72#endif
73
Avi Drissman5b286372020-07-28 21:59:3874#if defined(OS_APPLE)
[email protected]da9ccfb2012-01-28 00:34:4075Provider base_provider_mac = {
brettwf0dea132015-09-25 20:08:5476 PathProviderMac,
[email protected]5af2edb92008-08-08 20:16:0877 &base_provider,
78#ifndef NDEBUG
brettwf0dea132015-09-25 20:08:5479 PATH_MAC_START,
80 PATH_MAC_END,
[email protected]5af2edb92008-08-08 20:16:0881#endif
[email protected]173cb8a02008-08-20 15:47:3982 true
83};
[email protected]5af2edb92008-08-08 20:16:0884#endif
[email protected]4c0040c2008-08-15 01:04:1185
[email protected]f7d69972011-06-21 22:34:5086#if defined(OS_ANDROID)
[email protected]da9ccfb2012-01-28 00:34:4087Provider base_provider_android = {
brettwf0dea132015-09-25 20:08:5488 PathProviderAndroid,
[email protected]f7d69972011-06-21 22:34:5089 &base_provider,
90#ifndef NDEBUG
brettwf0dea132015-09-25 20:08:5491 PATH_ANDROID_START,
92 PATH_ANDROID_END,
[email protected]f7d69972011-06-21 22:34:5093#endif
94 true
95};
96#endif
97
scottmg1ab7aa82017-05-25 05:22:4998#if defined(OS_FUCHSIA)
99Provider base_provider_fuchsia = {PathProviderFuchsia, &base_provider,
100#ifndef NDEBUG
101 0, 0,
102#endif
103 true};
104#endif
105
Avi Drissman5b286372020-07-28 21:59:38106#if defined(OS_POSIX) && !defined(OS_APPLE) && !defined(OS_ANDROID) && \
scottmg1ab7aa82017-05-25 05:22:49107 !defined(OS_FUCHSIA)
[email protected]da9ccfb2012-01-28 00:34:40108Provider base_provider_posix = {
brettwf0dea132015-09-25 20:08:54109 PathProviderPosix,
[email protected]4c0040c2008-08-15 01:04:11110 &base_provider,
111#ifndef NDEBUG
brettwf0dea132015-09-25 20:08:54112 PATH_POSIX_START,
113 PATH_POSIX_END,
[email protected]4c0040c2008-08-15 01:04:11114#endif
[email protected]173cb8a02008-08-20 15:47:39115 true
116};
[email protected]4c0040c2008-08-15 01:04:11117#endif
118
119
initial.commitd7cae122008-07-26 21:49:38120struct PathData {
brettwf0dea132015-09-25 20:08:54121 Lock lock;
[email protected]20305ec2011-01-21 04:55:52122 PathMap cache; // Cache mappings from path key to path value.
123 PathMap overrides; // Track path overrides.
initial.commitd7cae122008-07-26 21:49:38124 Provider* providers; // Linked list of path service providers.
[email protected]c5a726b32013-01-29 00:56:56125 bool cache_disabled; // Don't use cache if true;
initial.commitd7cae122008-07-26 21:49:38126
[email protected]c5a726b32013-01-29 00:56:56127 PathData() : cache_disabled(false) {
[email protected]ac510e12008-08-05 19:46:31128#if defined(OS_WIN)
129 providers = &base_provider_win;
Avi Drissman5b286372020-07-28 21:59:38130#elif defined(OS_APPLE)
[email protected]5af2edb92008-08-08 20:16:08131 providers = &base_provider_mac;
[email protected]f7d69972011-06-21 22:34:50132#elif defined(OS_ANDROID)
133 providers = &base_provider_android;
scottmg1ab7aa82017-05-25 05:22:49134#elif defined(OS_FUCHSIA)
135 providers = &base_provider_fuchsia;
[email protected]5d1937bb2009-11-21 01:29:00136#elif defined(OS_POSIX)
137 providers = &base_provider_posix;
[email protected]ac510e12008-08-05 19:46:31138#endif
initial.commitd7cae122008-07-26 21:49:38139 }
140};
[email protected]52a261f2009-03-03 15:01:12141
[email protected]1265917f2008-08-12 17:33:52142static PathData* GetPathData() {
vmpstr5f02eae2017-02-13 21:11:41143 static auto* path_data = new PathData();
scottmg6ece5ae2017-02-01 18:25:19144 return path_data;
[email protected]1265917f2008-08-12 17:33:52145}
initial.commitd7cae122008-07-26 21:49:38146
Benoit Lize25859152020-07-09 11:52:09147// Tries to find |key| in the cache.
148bool LockedGetFromCache(int key, const PathData* path_data, FilePath* result)
149 EXCLUSIVE_LOCKS_REQUIRED(path_data->lock) {
[email protected]c5a726b32013-01-29 00:56:56150 if (path_data->cache_disabled)
151 return false;
[email protected]6723f832008-08-11 15:38:27152 // check for a cached version
jdoerrie1c4b8ff2018-10-03 00:10:57153 auto it = path_data->cache.find(key);
[email protected]6723f832008-08-11 15:38:27154 if (it != path_data->cache.end()) {
155 *result = it->second;
156 return true;
157 }
158 return false;
159}
160
Benoit Lize25859152020-07-09 11:52:09161// Tries to find |key| in the overrides map.
162bool LockedGetFromOverrides(int key, PathData* path_data, FilePath* result)
163 EXCLUSIVE_LOCKS_REQUIRED(path_data->lock) {
[email protected]846c3ecea2011-12-14 18:47:26164 // check for an overridden version.
[email protected]34e043b2010-09-09 23:49:04165 PathMap::const_iterator it = path_data->overrides.find(key);
166 if (it != path_data->overrides.end()) {
[email protected]c5a726b32013-01-29 00:56:56167 if (!path_data->cache_disabled)
168 path_data->cache[key] = it->second;
[email protected]34e043b2010-09-09 23:49:04169 *result = it->second;
170 return true;
171 }
172 return false;
173}
174
[email protected]d6b3af92012-09-26 19:05:12175} // namespace
[email protected]6723f832008-08-11 15:38:27176
initial.commitd7cae122008-07-26 21:49:38177// TODO(brettw): this function does not handle long paths (filename > MAX_PATH)
178// characters). This isn't supported very well by Windows right now, so it is
179// moot, but we should keep this in mind for the future.
180// static
[email protected]640517f2008-10-30 23:54:04181bool PathService::Get(int key, FilePath* result) {
[email protected]1265917f2008-08-12 17:33:52182 PathData* path_data = GetPathData();
initial.commitd7cae122008-07-26 21:49:38183 DCHECK(path_data);
184 DCHECK(result);
brettwf0dea132015-09-25 20:08:54185 DCHECK_GE(key, DIR_CURRENT);
initial.commitd7cae122008-07-26 21:49:38186
187 // special case the current directory because it can never be cached
brettwf0dea132015-09-25 20:08:54188 if (key == DIR_CURRENT)
189 return GetCurrentDirectory(result);
initial.commitd7cae122008-07-26 21:49:38190
Ivan Kotenkova16212a52017-11-08 12:37:33191 Provider* provider = nullptr;
[email protected]d6b3af92012-09-26 19:05:12192 {
brettwf0dea132015-09-25 20:08:54193 AutoLock scoped_lock(path_data->lock);
[email protected]d6b3af92012-09-26 19:05:12194 if (LockedGetFromCache(key, path_data, result))
195 return true;
[email protected]c1a9f8d42009-02-28 01:49:55196
[email protected]d6b3af92012-09-26 19:05:12197 if (LockedGetFromOverrides(key, path_data, result))
198 return true;
199
200 // Get the beginning of the list while it is still locked.
201 provider = path_data->providers;
202 }
[email protected]34e043b2010-09-09 23:49:04203
[email protected]4792a262008-11-19 16:50:03204 FilePath path;
initial.commitd7cae122008-07-26 21:49:38205
[email protected]d6b3af92012-09-26 19:05:12206 // Iterating does not need the lock because only the list head might be
207 // modified on another thread.
initial.commitd7cae122008-07-26 21:49:38208 while (provider) {
[email protected]4792a262008-11-19 16:50:03209 if (provider->func(key, &path))
initial.commitd7cae122008-07-26 21:49:38210 break;
[email protected]c1a9f8d42009-02-28 01:49:55211 DCHECK(path.empty()) << "provider should not have modified path";
initial.commitd7cae122008-07-26 21:49:38212 provider = provider->next;
213 }
214
[email protected]c1a9f8d42009-02-28 01:49:55215 if (path.empty())
initial.commitd7cae122008-07-26 21:49:38216 return false;
217
[email protected]082f8202013-01-26 04:51:29218 if (path.ReferencesParent()) {
219 // Make sure path service never returns a path with ".." in it.
[email protected]15476932013-04-12 05:17:15220 path = MakeAbsoluteFilePath(path);
221 if (path.empty())
[email protected]082f8202013-01-26 04:51:29222 return false;
[email protected]082f8202013-01-26 04:51:29223 }
[email protected]640517f2008-10-30 23:54:04224 *result = path;
[email protected]d6b3af92012-09-26 19:05:12225
brettwf0dea132015-09-25 20:08:54226 AutoLock scoped_lock(path_data->lock);
[email protected]c5a726b32013-01-29 00:56:56227 if (!path_data->cache_disabled)
228 path_data->cache[key] = path;
[email protected]d6b3af92012-09-26 19:05:12229
[email protected]640517f2008-10-30 23:54:04230 return true;
231}
232
Maksim Ivanov9a39401c2020-07-08 19:30:37233FilePath PathService::CheckedGet(int key) {
234 FilePath path;
Maksim Ivanoveeb2d0802020-07-17 21:44:00235 LOG_IF(FATAL, !Get(key, &path)) << "Failed to get the path for " << key;
Maksim Ivanov9a39401c2020-07-08 19:30:37236 return path;
237}
238
[email protected]d6b3af92012-09-26 19:05:12239// static
[email protected]eca6a4f2009-06-25 17:29:09240bool PathService::Override(int key, const FilePath& path) {
[email protected]ff9ed9f2014-05-02 17:59:42241 // Just call the full function with true for the value of |create|, and
242 // assume that |path| may not be absolute yet.
243 return OverrideAndCreateIfNeeded(key, path, false, true);
[email protected]cb571e752012-05-09 10:50:10244}
245
[email protected]d6b3af92012-09-26 19:05:12246// static
[email protected]cb571e752012-05-09 10:50:10247bool PathService::OverrideAndCreateIfNeeded(int key,
248 const FilePath& path,
[email protected]ff9ed9f2014-05-02 17:59:42249 bool is_absolute,
[email protected]cb571e752012-05-09 10:50:10250 bool create) {
[email protected]1265917f2008-08-12 17:33:52251 PathData* path_data = GetPathData();
initial.commitd7cae122008-07-26 21:49:38252 DCHECK(path_data);
brettwf0dea132015-09-25 20:08:54253 DCHECK_GT(key, DIR_CURRENT) << "invalid path key";
initial.commitd7cae122008-07-26 21:49:38254
[email protected]eca6a4f2009-06-25 17:29:09255 FilePath file_path = path;
initial.commitd7cae122008-07-26 21:49:38256
[email protected]cb571e752012-05-09 10:50:10257 // For some locations this will fail if called from inside the sandbox there-
258 // fore we protect this call with a flag.
259 if (create) {
260 // Make sure the directory exists. We need to do this before we translate
[email protected]15476932013-04-12 05:17:15261 // this to the absolute path because on POSIX, MakeAbsoluteFilePath fails
262 // if called on a non-existent path.
brettwf0dea132015-09-25 20:08:54263 if (!PathExists(file_path) && !CreateDirectory(file_path))
[email protected]cb571e752012-05-09 10:50:10264 return false;
265 }
initial.commitd7cae122008-07-26 21:49:38266
[email protected]15476932013-04-12 05:17:15267 // We need to have an absolute path.
[email protected]ff9ed9f2014-05-02 17:59:42268 if (!is_absolute) {
269 file_path = MakeAbsoluteFilePath(file_path);
270 if (file_path.empty())
271 return false;
272 }
273 DCHECK(file_path.IsAbsolute());
[email protected]dabdb682009-10-27 23:31:36274
brettwf0dea132015-09-25 20:08:54275 AutoLock scoped_lock(path_data->lock);
[email protected]34e043b2010-09-09 23:49:04276
277 // Clear the cache now. Some of its entries could have depended
278 // on the value we are overriding, and are now out of sync with reality.
279 path_data->cache.clear();
280
[email protected]34e043b2010-09-09 23:49:04281 path_data->overrides[key] = file_path;
282
initial.commitd7cae122008-07-26 21:49:38283 return true;
284}
285
[email protected]d6b3af92012-09-26 19:05:12286// static
mlcui3f0b3892020-12-10 00:11:13287bool PathService::RemoveOverrideForTests(int key) {
[email protected]d6b3af92012-09-26 19:05:12288 PathData* path_data = GetPathData();
289 DCHECK(path_data);
290
brettwf0dea132015-09-25 20:08:54291 AutoLock scoped_lock(path_data->lock);
[email protected]d6b3af92012-09-26 19:05:12292
293 if (path_data->overrides.find(key) == path_data->overrides.end())
294 return false;
295
296 // Clear the cache now. Some of its entries could have depended on the value
297 // we are going to remove, and are now out of sync.
298 path_data->cache.clear();
299
300 path_data->overrides.erase(key);
301
302 return true;
303}
304
305// static
mlcui8a86199b2020-12-03 06:41:27306bool PathService::IsOverriddenForTests(int key) {
307 PathData* path_data = GetPathData();
308 DCHECK(path_data);
309
310 AutoLock scoped_lock(path_data->lock);
311
312 return path_data->overrides.find(key) != path_data->overrides.end();
313}
314
315// static
initial.commitd7cae122008-07-26 21:49:38316void PathService::RegisterProvider(ProviderFunc func, int key_start,
317 int key_end) {
[email protected]1265917f2008-08-12 17:33:52318 PathData* path_data = GetPathData();
initial.commitd7cae122008-07-26 21:49:38319 DCHECK(path_data);
[email protected]88563f62011-03-13 22:13:33320 DCHECK_GT(key_end, key_start);
initial.commitd7cae122008-07-26 21:49:38321
initial.commitd7cae122008-07-26 21:49:38322 Provider* p;
323
initial.commitd7cae122008-07-26 21:49:38324 p = new Provider;
[email protected]173cb8a02008-08-20 15:47:39325 p->is_static = false;
initial.commitd7cae122008-07-26 21:49:38326 p->func = func;
initial.commitd7cae122008-07-26 21:49:38327#ifndef NDEBUG
328 p->key_start = key_start;
329 p->key_end = key_end;
330#endif
[email protected]d6b3af92012-09-26 19:05:12331
brettwf0dea132015-09-25 20:08:54332 AutoLock scoped_lock(path_data->lock);
[email protected]d6b3af92012-09-26 19:05:12333
334#ifndef NDEBUG
335 Provider *iter = path_data->providers;
336 while (iter) {
337 DCHECK(key_start >= iter->key_end || key_end <= iter->key_start) <<
338 "path provider collision";
339 iter = iter->next;
340 }
341#endif
342
343 p->next = path_data->providers;
initial.commitd7cae122008-07-26 21:49:38344 path_data->providers = p;
345}
[email protected]c5a726b32013-01-29 00:56:56346
347// static
348void PathService::DisableCache() {
349 PathData* path_data = GetPathData();
350 DCHECK(path_data);
351
brettwf0dea132015-09-25 20:08:54352 AutoLock scoped_lock(path_data->lock);
[email protected]c5a726b32013-01-29 00:56:56353 path_data->cache.clear();
354 path_data->cache_disabled = true;
355}
brettwf0dea132015-09-25 20:08:54356
357} // namespace base