blob: 6ac501eafeeb464d4592e2815b2c4e5a34b4df82 [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
[email protected]57999812013-02-24 05:40:5215#include "base/files/file_path.h"
[email protected]e3177dd52014-08-13 20:22:1416#include "base/files/file_util.h"
initial.commitd7cae122008-07-26 21:49:3817#include "base/logging.h"
[email protected]20305ec2011-01-21 04:55:5218#include "base/synchronization/lock.h"
avi9b6f42932015-12-26 22:15:1419#include "build/build_config.h"
initial.commitd7cae122008-07-26 21:49:3820
21namespace base {
brettwf0dea132015-09-25 20:08:5422
23bool PathProvider(int key, FilePath* result);
24
[email protected]5af2edb92008-08-08 20:16:0825#if defined(OS_WIN)
brettwf0dea132015-09-25 20:08:5426bool PathProviderWin(int key, FilePath* result);
[email protected]4c0040c2008-08-15 01:04:1127#elif defined(OS_MACOSX)
brettwf0dea132015-09-25 20:08:5428bool PathProviderMac(int key, FilePath* result);
[email protected]f7d69972011-06-21 22:34:5029#elif defined(OS_ANDROID)
brettwf0dea132015-09-25 20:08:5430bool PathProviderAndroid(int key, FilePath* result);
scottmg1ab7aa82017-05-25 05:22:4931#elif defined(OS_FUCHSIA)
32bool PathProviderFuchsia(int key, FilePath* result);
[email protected]5d1937bb2009-11-21 01:29:0033#elif defined(OS_POSIX)
brettwf0dea132015-09-25 20:08:5434// PathProviderPosix is the default path provider on POSIX OSes other than
35// Mac and Android.
36bool PathProviderPosix(int key, FilePath* result);
[email protected]ac510e12008-08-05 19:46:3137#endif
initial.commitd7cae122008-07-26 21:49:3838
39namespace {
40
brettw1ce49f62017-04-27 19:42:3241typedef std::unordered_map<int, FilePath> PathMap;
initial.commitd7cae122008-07-26 21:49:3842
43// We keep a linked list of providers. In a debug build we ensure that no two
44// providers claim overlapping keys.
45struct Provider {
46 PathService::ProviderFunc func;
47 struct Provider* next;
48#ifndef NDEBUG
49 int key_start;
50 int key_end;
51#endif
[email protected]173cb8a02008-08-20 15:47:3952 bool is_static;
initial.commitd7cae122008-07-26 21:49:3853};
54
Ivan Kotenkova16212a52017-11-08 12:37:3355Provider base_provider = {PathProvider, nullptr,
initial.commitd7cae122008-07-26 21:49:3856#ifndef NDEBUG
Ivan Kotenkova16212a52017-11-08 12:37:3357 PATH_START, PATH_END,
initial.commitd7cae122008-07-26 21:49:3858#endif
Ivan Kotenkova16212a52017-11-08 12:37:3359 true};
initial.commitd7cae122008-07-26 21:49:3860
[email protected]5d1937bb2009-11-21 01:29:0061#if defined(OS_WIN)
[email protected]da9ccfb2012-01-28 00:34:4062Provider base_provider_win = {
brettwf0dea132015-09-25 20:08:5463 PathProviderWin,
[email protected]ac510e12008-08-05 19:46:3164 &base_provider,
65#ifndef NDEBUG
brettwf0dea132015-09-25 20:08:5466 PATH_WIN_START,
67 PATH_WIN_END,
[email protected]ac510e12008-08-05 19:46:3168#endif
[email protected]173cb8a02008-08-20 15:47:3969 true
[email protected]ac510e12008-08-05 19:46:3170};
71#endif
72
[email protected]5d1937bb2009-11-21 01:29:0073#if defined(OS_MACOSX)
[email protected]da9ccfb2012-01-28 00:34:4074Provider base_provider_mac = {
brettwf0dea132015-09-25 20:08:5475 PathProviderMac,
[email protected]5af2edb92008-08-08 20:16:0876 &base_provider,
77#ifndef NDEBUG
brettwf0dea132015-09-25 20:08:5478 PATH_MAC_START,
79 PATH_MAC_END,
[email protected]5af2edb92008-08-08 20:16:0880#endif
[email protected]173cb8a02008-08-20 15:47:3981 true
82};
[email protected]5af2edb92008-08-08 20:16:0883#endif
[email protected]4c0040c2008-08-15 01:04:1184
[email protected]f7d69972011-06-21 22:34:5085#if defined(OS_ANDROID)
[email protected]da9ccfb2012-01-28 00:34:4086Provider base_provider_android = {
brettwf0dea132015-09-25 20:08:5487 PathProviderAndroid,
[email protected]f7d69972011-06-21 22:34:5088 &base_provider,
89#ifndef NDEBUG
brettwf0dea132015-09-25 20:08:5490 PATH_ANDROID_START,
91 PATH_ANDROID_END,
[email protected]f7d69972011-06-21 22:34:5092#endif
93 true
94};
95#endif
96
scottmg1ab7aa82017-05-25 05:22:4997#if defined(OS_FUCHSIA)
98Provider 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)
[email protected]da9ccfb2012-01-28 00:34:40107Provider base_provider_posix = {
brettwf0dea132015-09-25 20:08:54108 PathProviderPosix,
[email protected]4c0040c2008-08-15 01:04:11109 &base_provider,
110#ifndef NDEBUG
brettwf0dea132015-09-25 20:08:54111 PATH_POSIX_START,
112 PATH_POSIX_END,
[email protected]4c0040c2008-08-15 01:04:11113#endif
[email protected]173cb8a02008-08-20 15:47:39114 true
115};
[email protected]4c0040c2008-08-15 01:04:11116#endif
117
118
initial.commitd7cae122008-07-26 21:49:38119struct PathData {
brettwf0dea132015-09-25 20:08:54120 Lock lock;
[email protected]20305ec2011-01-21 04:55:52121 PathMap cache; // Cache mappings from path key to path value.
122 PathMap overrides; // Track path overrides.
initial.commitd7cae122008-07-26 21:49:38123 Provider* providers; // Linked list of path service providers.
[email protected]c5a726b32013-01-29 00:56:56124 bool cache_disabled; // Don't use cache if true;
initial.commitd7cae122008-07-26 21:49:38125
[email protected]c5a726b32013-01-29 00:56:56126 PathData() : cache_disabled(false) {
[email protected]ac510e12008-08-05 19:46:31127#if defined(OS_WIN)
128 providers = &base_provider_win;
[email protected]5af2edb92008-08-08 20:16:08129#elif defined(OS_MACOSX)
130 providers = &base_provider_mac;
[email protected]f7d69972011-06-21 22:34:50131#elif defined(OS_ANDROID)
132 providers = &base_provider_android;
scottmg1ab7aa82017-05-25 05:22:49133#elif defined(OS_FUCHSIA)
134 providers = &base_provider_fuchsia;
[email protected]5d1937bb2009-11-21 01:29:00135#elif defined(OS_POSIX)
136 providers = &base_provider_posix;
[email protected]ac510e12008-08-05 19:46:31137#endif
initial.commitd7cae122008-07-26 21:49:38138 }
139};
[email protected]52a261f2009-03-03 15:01:12140
[email protected]1265917f2008-08-12 17:33:52141static PathData* GetPathData() {
vmpstr5f02eae2017-02-13 21:11:41142 static auto* path_data = new PathData();
scottmg6ece5ae2017-02-01 18:25:19143 return path_data;
[email protected]1265917f2008-08-12 17:33:52144}
initial.commitd7cae122008-07-26 21:49:38145
[email protected]d6b3af92012-09-26 19:05:12146// Tries to find |key| in the cache. |path_data| should be locked by the caller!
147bool LockedGetFromCache(int key, const PathData* path_data, FilePath* result) {
[email protected]c5a726b32013-01-29 00:56:56148 if (path_data->cache_disabled)
149 return false;
[email protected]6723f832008-08-11 15:38:27150 // 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
[email protected]d6b3af92012-09-26 19:05:12159// Tries to find |key| in the overrides map. |path_data| should be locked by the
160// caller!
161bool LockedGetFromOverrides(int key, PathData* path_data, FilePath* result) {
[email protected]846c3ecea2011-12-14 18:47:26162 // check for an overridden version.
[email protected]34e043b2010-09-09 23:49:04163 PathMap::const_iterator it = path_data->overrides.find(key);
164 if (it != path_data->overrides.end()) {
[email protected]c5a726b32013-01-29 00:56:56165 if (!path_data->cache_disabled)
166 path_data->cache[key] = it->second;
[email protected]34e043b2010-09-09 23:49:04167 *result = it->second;
168 return true;
169 }
170 return false;
171}
172
[email protected]d6b3af92012-09-26 19:05:12173} // namespace
[email protected]6723f832008-08-11 15:38:27174
initial.commitd7cae122008-07-26 21:49:38175// 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
[email protected]640517f2008-10-30 23:54:04179bool PathService::Get(int key, FilePath* result) {
[email protected]1265917f2008-08-12 17:33:52180 PathData* path_data = GetPathData();
initial.commitd7cae122008-07-26 21:49:38181 DCHECK(path_data);
182 DCHECK(result);
brettwf0dea132015-09-25 20:08:54183 DCHECK_GE(key, DIR_CURRENT);
initial.commitd7cae122008-07-26 21:49:38184
185 // special case the current directory because it can never be cached
brettwf0dea132015-09-25 20:08:54186 if (key == DIR_CURRENT)
187 return GetCurrentDirectory(result);
initial.commitd7cae122008-07-26 21:49:38188
Ivan Kotenkova16212a52017-11-08 12:37:33189 Provider* provider = nullptr;
[email protected]d6b3af92012-09-26 19:05:12190 {
brettwf0dea132015-09-25 20:08:54191 AutoLock scoped_lock(path_data->lock);
[email protected]d6b3af92012-09-26 19:05:12192 if (LockedGetFromCache(key, path_data, result))
193 return true;
[email protected]c1a9f8d42009-02-28 01:49:55194
[email protected]d6b3af92012-09-26 19:05:12195 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 }
[email protected]34e043b2010-09-09 23:49:04201
[email protected]4792a262008-11-19 16:50:03202 FilePath path;
initial.commitd7cae122008-07-26 21:49:38203
[email protected]d6b3af92012-09-26 19:05:12204 // Iterating does not need the lock because only the list head might be
205 // modified on another thread.
initial.commitd7cae122008-07-26 21:49:38206 while (provider) {
[email protected]4792a262008-11-19 16:50:03207 if (provider->func(key, &path))
initial.commitd7cae122008-07-26 21:49:38208 break;
[email protected]c1a9f8d42009-02-28 01:49:55209 DCHECK(path.empty()) << "provider should not have modified path";
initial.commitd7cae122008-07-26 21:49:38210 provider = provider->next;
211 }
212
[email protected]c1a9f8d42009-02-28 01:49:55213 if (path.empty())
initial.commitd7cae122008-07-26 21:49:38214 return false;
215
[email protected]082f8202013-01-26 04:51:29216 if (path.ReferencesParent()) {
217 // Make sure path service never returns a path with ".." in it.
[email protected]15476932013-04-12 05:17:15218 path = MakeAbsoluteFilePath(path);
219 if (path.empty())
[email protected]082f8202013-01-26 04:51:29220 return false;
[email protected]082f8202013-01-26 04:51:29221 }
[email protected]640517f2008-10-30 23:54:04222 *result = path;
[email protected]d6b3af92012-09-26 19:05:12223
brettwf0dea132015-09-25 20:08:54224 AutoLock scoped_lock(path_data->lock);
[email protected]c5a726b32013-01-29 00:56:56225 if (!path_data->cache_disabled)
226 path_data->cache[key] = path;
[email protected]d6b3af92012-09-26 19:05:12227
[email protected]640517f2008-10-30 23:54:04228 return true;
229}
230
[email protected]d6b3af92012-09-26 19:05:12231// static
[email protected]eca6a4f2009-06-25 17:29:09232bool PathService::Override(int key, const FilePath& path) {
[email protected]ff9ed9f2014-05-02 17:59:42233 // 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);
[email protected]cb571e752012-05-09 10:50:10236}
237
[email protected]d6b3af92012-09-26 19:05:12238// static
[email protected]cb571e752012-05-09 10:50:10239bool PathService::OverrideAndCreateIfNeeded(int key,
240 const FilePath& path,
[email protected]ff9ed9f2014-05-02 17:59:42241 bool is_absolute,
[email protected]cb571e752012-05-09 10:50:10242 bool create) {
[email protected]1265917f2008-08-12 17:33:52243 PathData* path_data = GetPathData();
initial.commitd7cae122008-07-26 21:49:38244 DCHECK(path_data);
brettwf0dea132015-09-25 20:08:54245 DCHECK_GT(key, DIR_CURRENT) << "invalid path key";
initial.commitd7cae122008-07-26 21:49:38246
[email protected]eca6a4f2009-06-25 17:29:09247 FilePath file_path = path;
initial.commitd7cae122008-07-26 21:49:38248
[email protected]cb571e752012-05-09 10:50:10249 // 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
[email protected]15476932013-04-12 05:17:15253 // this to the absolute path because on POSIX, MakeAbsoluteFilePath fails
254 // if called on a non-existent path.
brettwf0dea132015-09-25 20:08:54255 if (!PathExists(file_path) && !CreateDirectory(file_path))
[email protected]cb571e752012-05-09 10:50:10256 return false;
257 }
initial.commitd7cae122008-07-26 21:49:38258
[email protected]15476932013-04-12 05:17:15259 // We need to have an absolute path.
[email protected]ff9ed9f2014-05-02 17:59:42260 if (!is_absolute) {
261 file_path = MakeAbsoluteFilePath(file_path);
262 if (file_path.empty())
263 return false;
264 }
265 DCHECK(file_path.IsAbsolute());
[email protected]dabdb682009-10-27 23:31:36266
brettwf0dea132015-09-25 20:08:54267 AutoLock scoped_lock(path_data->lock);
[email protected]34e043b2010-09-09 23:49:04268
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
[email protected]34e043b2010-09-09 23:49:04273 path_data->overrides[key] = file_path;
274
initial.commitd7cae122008-07-26 21:49:38275 return true;
276}
277
[email protected]d6b3af92012-09-26 19:05:12278// static
279bool PathService::RemoveOverride(int key) {
280 PathData* path_data = GetPathData();
281 DCHECK(path_data);
282
brettwf0dea132015-09-25 20:08:54283 AutoLock scoped_lock(path_data->lock);
[email protected]d6b3af92012-09-26 19:05:12284
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
initial.commitd7cae122008-07-26 21:49:38298void PathService::RegisterProvider(ProviderFunc func, int key_start,
299 int key_end) {
[email protected]1265917f2008-08-12 17:33:52300 PathData* path_data = GetPathData();
initial.commitd7cae122008-07-26 21:49:38301 DCHECK(path_data);
[email protected]88563f62011-03-13 22:13:33302 DCHECK_GT(key_end, key_start);
initial.commitd7cae122008-07-26 21:49:38303
initial.commitd7cae122008-07-26 21:49:38304 Provider* p;
305
initial.commitd7cae122008-07-26 21:49:38306 p = new Provider;
[email protected]173cb8a02008-08-20 15:47:39307 p->is_static = false;
initial.commitd7cae122008-07-26 21:49:38308 p->func = func;
initial.commitd7cae122008-07-26 21:49:38309#ifndef NDEBUG
310 p->key_start = key_start;
311 p->key_end = key_end;
312#endif
[email protected]d6b3af92012-09-26 19:05:12313
brettwf0dea132015-09-25 20:08:54314 AutoLock scoped_lock(path_data->lock);
[email protected]d6b3af92012-09-26 19:05:12315
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;
initial.commitd7cae122008-07-26 21:49:38326 path_data->providers = p;
327}
[email protected]c5a726b32013-01-29 00:56:56328
329// static
330void PathService::DisableCache() {
331 PathData* path_data = GetPathData();
332 DCHECK(path_data);
333
brettwf0dea132015-09-25 20:08:54334 AutoLock scoped_lock(path_data->lock);
[email protected]c5a726b32013-01-29 00:56:56335 path_data->cache.clear();
336 path_data->cache_disabled = true;
337}
brettwf0dea132015-09-25 20:08:54338
339} // namespace base