blob: 3f954d79e4bd8f4a84f80a505eb2fdde93577254 [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
[email protected]31662202013-03-23 19:10:547#if defined(OS_WIN)
initial.commitd7cae122008-07-26 21:49:388#include <windows.h>
9#include <shellapi.h>
10#include <shlobj.h>
[email protected]355cc2742008-08-06 16:01:2511#endif
initial.commitd7cae122008-07-26 21:49:3812
[email protected]14c1c232013-06-11 17:52:4413#include "base/containers/hash_tables.h"
[email protected]57999812013-02-24 05:40:5214#include "base/files/file_path.h"
[email protected]e3177dd52014-08-13 20:22:1415#include "base/files/file_util.h"
[email protected]625332e02010-12-14 07:48:4916#include "base/lazy_instance.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);
[email protected]5d1937bb2009-11-21 01:29:0031#elif defined(OS_POSIX)
brettwf0dea132015-09-25 20:08:5432// PathProviderPosix is the default path provider on POSIX OSes other than
33// Mac and Android.
34bool PathProviderPosix(int key, FilePath* result);
[email protected]ac510e12008-08-05 19:46:3135#endif
initial.commitd7cae122008-07-26 21:49:3836
37namespace {
38
brettwf0dea132015-09-25 20:08:5439typedef hash_map<int, FilePath> PathMap;
initial.commitd7cae122008-07-26 21:49:3840
41// We keep a linked list of providers. In a debug build we ensure that no two
42// providers claim overlapping keys.
43struct Provider {
44 PathService::ProviderFunc func;
45 struct Provider* next;
46#ifndef NDEBUG
47 int key_start;
48 int key_end;
49#endif
[email protected]173cb8a02008-08-20 15:47:3950 bool is_static;
initial.commitd7cae122008-07-26 21:49:3851};
52
[email protected]da9ccfb2012-01-28 00:34:4053Provider base_provider = {
brettwf0dea132015-09-25 20:08:5454 PathProvider,
initial.commitd7cae122008-07-26 21:49:3855 NULL,
56#ifndef NDEBUG
brettwf0dea132015-09-25 20:08:5457 PATH_START,
58 PATH_END,
initial.commitd7cae122008-07-26 21:49:3859#endif
[email protected]173cb8a02008-08-20 15:47:3960 true
initial.commitd7cae122008-07-26 21:49:3861};
62
[email protected]5d1937bb2009-11-21 01:29:0063#if defined(OS_WIN)
[email protected]da9ccfb2012-01-28 00:34:4064Provider base_provider_win = {
brettwf0dea132015-09-25 20:08:5465 PathProviderWin,
[email protected]ac510e12008-08-05 19:46:3166 &base_provider,
67#ifndef NDEBUG
brettwf0dea132015-09-25 20:08:5468 PATH_WIN_START,
69 PATH_WIN_END,
[email protected]ac510e12008-08-05 19:46:3170#endif
[email protected]173cb8a02008-08-20 15:47:3971 true
[email protected]ac510e12008-08-05 19:46:3172};
73#endif
74
[email protected]5d1937bb2009-11-21 01:29:0075#if defined(OS_MACOSX)
[email protected]da9ccfb2012-01-28 00:34:4076Provider base_provider_mac = {
brettwf0dea132015-09-25 20:08:5477 PathProviderMac,
[email protected]5af2edb92008-08-08 20:16:0878 &base_provider,
79#ifndef NDEBUG
brettwf0dea132015-09-25 20:08:5480 PATH_MAC_START,
81 PATH_MAC_END,
[email protected]5af2edb92008-08-08 20:16:0882#endif
[email protected]173cb8a02008-08-20 15:47:3983 true
84};
[email protected]5af2edb92008-08-08 20:16:0885#endif
[email protected]4c0040c2008-08-15 01:04:1186
[email protected]f7d69972011-06-21 22:34:5087#if defined(OS_ANDROID)
[email protected]da9ccfb2012-01-28 00:34:4088Provider base_provider_android = {
brettwf0dea132015-09-25 20:08:5489 PathProviderAndroid,
[email protected]f7d69972011-06-21 22:34:5090 &base_provider,
91#ifndef NDEBUG
brettwf0dea132015-09-25 20:08:5492 PATH_ANDROID_START,
93 PATH_ANDROID_END,
[email protected]f7d69972011-06-21 22:34:5094#endif
95 true
96};
97#endif
98
99#if defined(OS_POSIX) && !defined(OS_MACOSX) && !defined(OS_ANDROID)
[email protected]da9ccfb2012-01-28 00:34:40100Provider base_provider_posix = {
brettwf0dea132015-09-25 20:08:54101 PathProviderPosix,
[email protected]4c0040c2008-08-15 01:04:11102 &base_provider,
103#ifndef NDEBUG
brettwf0dea132015-09-25 20:08:54104 PATH_POSIX_START,
105 PATH_POSIX_END,
[email protected]4c0040c2008-08-15 01:04:11106#endif
[email protected]173cb8a02008-08-20 15:47:39107 true
108};
[email protected]4c0040c2008-08-15 01:04:11109#endif
110
111
initial.commitd7cae122008-07-26 21:49:38112struct PathData {
brettwf0dea132015-09-25 20:08:54113 Lock lock;
[email protected]20305ec2011-01-21 04:55:52114 PathMap cache; // Cache mappings from path key to path value.
115 PathMap overrides; // Track path overrides.
initial.commitd7cae122008-07-26 21:49:38116 Provider* providers; // Linked list of path service providers.
[email protected]c5a726b32013-01-29 00:56:56117 bool cache_disabled; // Don't use cache if true;
initial.commitd7cae122008-07-26 21:49:38118
[email protected]c5a726b32013-01-29 00:56:56119 PathData() : cache_disabled(false) {
[email protected]ac510e12008-08-05 19:46:31120#if defined(OS_WIN)
121 providers = &base_provider_win;
[email protected]5af2edb92008-08-08 20:16:08122#elif defined(OS_MACOSX)
123 providers = &base_provider_mac;
[email protected]f7d69972011-06-21 22:34:50124#elif defined(OS_ANDROID)
125 providers = &base_provider_android;
[email protected]5d1937bb2009-11-21 01:29:00126#elif defined(OS_POSIX)
127 providers = &base_provider_posix;
[email protected]ac510e12008-08-05 19:46:31128#endif
initial.commitd7cae122008-07-26 21:49:38129 }
130};
[email protected]52a261f2009-03-03 15:01:12131
Scott Graham4189fab2015-12-03 23:11:38132static LazyInstance<PathData>::Leaky g_path_data = LAZY_INSTANCE_INITIALIZER;
[email protected]625332e02010-12-14 07:48:49133
[email protected]1265917f2008-08-12 17:33:52134static PathData* GetPathData() {
[email protected]625332e02010-12-14 07:48:49135 return g_path_data.Pointer();
[email protected]1265917f2008-08-12 17:33:52136}
initial.commitd7cae122008-07-26 21:49:38137
[email protected]d6b3af92012-09-26 19:05:12138// Tries to find |key| in the cache. |path_data| should be locked by the caller!
139bool LockedGetFromCache(int key, const PathData* path_data, FilePath* result) {
[email protected]c5a726b32013-01-29 00:56:56140 if (path_data->cache_disabled)
141 return false;
[email protected]6723f832008-08-11 15:38:27142 // check for a cached version
143 PathMap::const_iterator it = path_data->cache.find(key);
144 if (it != path_data->cache.end()) {
145 *result = it->second;
146 return true;
147 }
148 return false;
149}
150
[email protected]d6b3af92012-09-26 19:05:12151// Tries to find |key| in the overrides map. |path_data| should be locked by the
152// caller!
153bool LockedGetFromOverrides(int key, PathData* path_data, FilePath* result) {
[email protected]846c3ecea2011-12-14 18:47:26154 // check for an overridden version.
[email protected]34e043b2010-09-09 23:49:04155 PathMap::const_iterator it = path_data->overrides.find(key);
156 if (it != path_data->overrides.end()) {
[email protected]c5a726b32013-01-29 00:56:56157 if (!path_data->cache_disabled)
158 path_data->cache[key] = it->second;
[email protected]34e043b2010-09-09 23:49:04159 *result = it->second;
160 return true;
161 }
162 return false;
163}
164
[email protected]d6b3af92012-09-26 19:05:12165} // namespace
[email protected]6723f832008-08-11 15:38:27166
initial.commitd7cae122008-07-26 21:49:38167// TODO(brettw): this function does not handle long paths (filename > MAX_PATH)
168// characters). This isn't supported very well by Windows right now, so it is
169// moot, but we should keep this in mind for the future.
170// static
[email protected]640517f2008-10-30 23:54:04171bool PathService::Get(int key, FilePath* result) {
[email protected]1265917f2008-08-12 17:33:52172 PathData* path_data = GetPathData();
initial.commitd7cae122008-07-26 21:49:38173 DCHECK(path_data);
174 DCHECK(result);
brettwf0dea132015-09-25 20:08:54175 DCHECK_GE(key, DIR_CURRENT);
initial.commitd7cae122008-07-26 21:49:38176
177 // special case the current directory because it can never be cached
brettwf0dea132015-09-25 20:08:54178 if (key == DIR_CURRENT)
179 return GetCurrentDirectory(result);
initial.commitd7cae122008-07-26 21:49:38180
[email protected]d6b3af92012-09-26 19:05:12181 Provider* provider = NULL;
182 {
brettwf0dea132015-09-25 20:08:54183 AutoLock scoped_lock(path_data->lock);
[email protected]d6b3af92012-09-26 19:05:12184 if (LockedGetFromCache(key, path_data, result))
185 return true;
[email protected]c1a9f8d42009-02-28 01:49:55186
[email protected]d6b3af92012-09-26 19:05:12187 if (LockedGetFromOverrides(key, path_data, result))
188 return true;
189
190 // Get the beginning of the list while it is still locked.
191 provider = path_data->providers;
192 }
[email protected]34e043b2010-09-09 23:49:04193
[email protected]4792a262008-11-19 16:50:03194 FilePath path;
initial.commitd7cae122008-07-26 21:49:38195
[email protected]d6b3af92012-09-26 19:05:12196 // Iterating does not need the lock because only the list head might be
197 // modified on another thread.
initial.commitd7cae122008-07-26 21:49:38198 while (provider) {
[email protected]4792a262008-11-19 16:50:03199 if (provider->func(key, &path))
initial.commitd7cae122008-07-26 21:49:38200 break;
[email protected]c1a9f8d42009-02-28 01:49:55201 DCHECK(path.empty()) << "provider should not have modified path";
initial.commitd7cae122008-07-26 21:49:38202 provider = provider->next;
203 }
204
[email protected]c1a9f8d42009-02-28 01:49:55205 if (path.empty())
initial.commitd7cae122008-07-26 21:49:38206 return false;
207
[email protected]082f8202013-01-26 04:51:29208 if (path.ReferencesParent()) {
209 // Make sure path service never returns a path with ".." in it.
[email protected]15476932013-04-12 05:17:15210 path = MakeAbsoluteFilePath(path);
211 if (path.empty())
[email protected]082f8202013-01-26 04:51:29212 return false;
[email protected]082f8202013-01-26 04:51:29213 }
[email protected]640517f2008-10-30 23:54:04214 *result = path;
[email protected]d6b3af92012-09-26 19:05:12215
brettwf0dea132015-09-25 20:08:54216 AutoLock scoped_lock(path_data->lock);
[email protected]c5a726b32013-01-29 00:56:56217 if (!path_data->cache_disabled)
218 path_data->cache[key] = path;
[email protected]d6b3af92012-09-26 19:05:12219
[email protected]640517f2008-10-30 23:54:04220 return true;
221}
222
[email protected]d6b3af92012-09-26 19:05:12223// static
[email protected]eca6a4f2009-06-25 17:29:09224bool PathService::Override(int key, const FilePath& path) {
[email protected]ff9ed9f2014-05-02 17:59:42225 // Just call the full function with true for the value of |create|, and
226 // assume that |path| may not be absolute yet.
227 return OverrideAndCreateIfNeeded(key, path, false, true);
[email protected]cb571e752012-05-09 10:50:10228}
229
[email protected]d6b3af92012-09-26 19:05:12230// static
[email protected]cb571e752012-05-09 10:50:10231bool PathService::OverrideAndCreateIfNeeded(int key,
232 const FilePath& path,
[email protected]ff9ed9f2014-05-02 17:59:42233 bool is_absolute,
[email protected]cb571e752012-05-09 10:50:10234 bool create) {
[email protected]1265917f2008-08-12 17:33:52235 PathData* path_data = GetPathData();
initial.commitd7cae122008-07-26 21:49:38236 DCHECK(path_data);
brettwf0dea132015-09-25 20:08:54237 DCHECK_GT(key, DIR_CURRENT) << "invalid path key";
initial.commitd7cae122008-07-26 21:49:38238
[email protected]eca6a4f2009-06-25 17:29:09239 FilePath file_path = path;
initial.commitd7cae122008-07-26 21:49:38240
[email protected]cb571e752012-05-09 10:50:10241 // For some locations this will fail if called from inside the sandbox there-
242 // fore we protect this call with a flag.
243 if (create) {
244 // Make sure the directory exists. We need to do this before we translate
[email protected]15476932013-04-12 05:17:15245 // this to the absolute path because on POSIX, MakeAbsoluteFilePath fails
246 // if called on a non-existent path.
brettwf0dea132015-09-25 20:08:54247 if (!PathExists(file_path) && !CreateDirectory(file_path))
[email protected]cb571e752012-05-09 10:50:10248 return false;
249 }
initial.commitd7cae122008-07-26 21:49:38250
[email protected]15476932013-04-12 05:17:15251 // We need to have an absolute path.
[email protected]ff9ed9f2014-05-02 17:59:42252 if (!is_absolute) {
253 file_path = MakeAbsoluteFilePath(file_path);
254 if (file_path.empty())
255 return false;
256 }
257 DCHECK(file_path.IsAbsolute());
[email protected]dabdb682009-10-27 23:31:36258
brettwf0dea132015-09-25 20:08:54259 AutoLock scoped_lock(path_data->lock);
[email protected]34e043b2010-09-09 23:49:04260
261 // Clear the cache now. Some of its entries could have depended
262 // on the value we are overriding, and are now out of sync with reality.
263 path_data->cache.clear();
264
[email protected]34e043b2010-09-09 23:49:04265 path_data->overrides[key] = file_path;
266
initial.commitd7cae122008-07-26 21:49:38267 return true;
268}
269
[email protected]d6b3af92012-09-26 19:05:12270// static
271bool PathService::RemoveOverride(int key) {
272 PathData* path_data = GetPathData();
273 DCHECK(path_data);
274
brettwf0dea132015-09-25 20:08:54275 AutoLock scoped_lock(path_data->lock);
[email protected]d6b3af92012-09-26 19:05:12276
277 if (path_data->overrides.find(key) == path_data->overrides.end())
278 return false;
279
280 // Clear the cache now. Some of its entries could have depended on the value
281 // we are going to remove, and are now out of sync.
282 path_data->cache.clear();
283
284 path_data->overrides.erase(key);
285
286 return true;
287}
288
289// static
initial.commitd7cae122008-07-26 21:49:38290void PathService::RegisterProvider(ProviderFunc func, int key_start,
291 int key_end) {
[email protected]1265917f2008-08-12 17:33:52292 PathData* path_data = GetPathData();
initial.commitd7cae122008-07-26 21:49:38293 DCHECK(path_data);
[email protected]88563f62011-03-13 22:13:33294 DCHECK_GT(key_end, key_start);
initial.commitd7cae122008-07-26 21:49:38295
initial.commitd7cae122008-07-26 21:49:38296 Provider* p;
297
initial.commitd7cae122008-07-26 21:49:38298 p = new Provider;
[email protected]173cb8a02008-08-20 15:47:39299 p->is_static = false;
initial.commitd7cae122008-07-26 21:49:38300 p->func = func;
initial.commitd7cae122008-07-26 21:49:38301#ifndef NDEBUG
302 p->key_start = key_start;
303 p->key_end = key_end;
304#endif
[email protected]d6b3af92012-09-26 19:05:12305
brettwf0dea132015-09-25 20:08:54306 AutoLock scoped_lock(path_data->lock);
[email protected]d6b3af92012-09-26 19:05:12307
308#ifndef NDEBUG
309 Provider *iter = path_data->providers;
310 while (iter) {
311 DCHECK(key_start >= iter->key_end || key_end <= iter->key_start) <<
312 "path provider collision";
313 iter = iter->next;
314 }
315#endif
316
317 p->next = path_data->providers;
initial.commitd7cae122008-07-26 21:49:38318 path_data->providers = p;
319}
[email protected]c5a726b32013-01-29 00:56:56320
321// static
322void PathService::DisableCache() {
323 PathData* path_data = GetPathData();
324 DCHECK(path_data);
325
brettwf0dea132015-09-25 20:08:54326 AutoLock scoped_lock(path_data->lock);
[email protected]c5a726b32013-01-29 00:56:56327 path_data->cache.clear();
328 path_data->cache_disabled = true;
329}
brettwf0dea132015-09-25 20:08:54330
331} // namespace base