blob: 1fd2f6f900402dedc98d07533cf891f0e574fd40 [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]09ad1e622008-08-07 20:23:0913#include "base/file_util.h"
[email protected]57999812013-02-24 05:40:5214#include "base/files/file_path.h"
[email protected]640517f2008-10-30 23:54:0415#include "base/hash_tables.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"
initial.commitd7cae122008-07-26 21:49:3819
[email protected]04af979a2013-02-16 04:12:2620using base::FilePath;
21
initial.commitd7cae122008-07-26 21:49:3822namespace base {
[email protected]4792a262008-11-19 16:50:0323 bool PathProvider(int key, FilePath* result);
[email protected]5af2edb92008-08-08 20:16:0824#if defined(OS_WIN)
[email protected]4792a262008-11-19 16:50:0325 bool PathProviderWin(int key, FilePath* result);
[email protected]4c0040c2008-08-15 01:04:1126#elif defined(OS_MACOSX)
[email protected]4792a262008-11-19 16:50:0327 bool PathProviderMac(int key, FilePath* result);
[email protected]f7d69972011-06-21 22:34:5028#elif defined(OS_ANDROID)
29 bool PathProviderAndroid(int key, FilePath* result);
[email protected]5d1937bb2009-11-21 01:29:0030#elif defined(OS_POSIX)
[email protected]dea1d7d2012-09-20 16:24:5231 // PathProviderPosix is the default path provider on POSIX OSes other than
32 // Mac and Android.
[email protected]5d1937bb2009-11-21 01:29:0033 bool PathProviderPosix(int key, FilePath* result);
[email protected]ac510e12008-08-05 19:46:3134#endif
initial.commitd7cae122008-07-26 21:49:3835}
36
37namespace {
38
[email protected]640517f2008-10-30 23:54:0439typedef base::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 = {
initial.commitd7cae122008-07-26 21:49:3854 base::PathProvider,
55 NULL,
56#ifndef NDEBUG
57 base::PATH_START,
[email protected]173cb8a02008-08-20 15:47:3958 base::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 = {
[email protected]ac510e12008-08-05 19:46:3165 base::PathProviderWin,
66 &base_provider,
67#ifndef NDEBUG
68 base::PATH_WIN_START,
[email protected]173cb8a02008-08-20 15:47:3969 base::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 = {
[email protected]5af2edb92008-08-08 20:16:0877 base::PathProviderMac,
78 &base_provider,
79#ifndef NDEBUG
80 base::PATH_MAC_START,
[email protected]173cb8a02008-08-20 15:47:3981 base::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 = {
[email protected]f7d69972011-06-21 22:34:5089 base::PathProviderAndroid,
90 &base_provider,
91#ifndef NDEBUG
[email protected]dea1d7d2012-09-20 16:24:5292 base::PATH_ANDROID_START,
93 base::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 = {
[email protected]5d1937bb2009-11-21 01:29:00101 base::PathProviderPosix,
[email protected]4c0040c2008-08-15 01:04:11102 &base_provider,
103#ifndef NDEBUG
[email protected]dea1d7d2012-09-20 16:24:52104 base::PATH_POSIX_START,
105 base::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 {
[email protected]20305ec2011-01-21 04:55:52113 base::Lock lock;
114 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 }
[email protected]173cb8a02008-08-20 15:47:39130
131 ~PathData() {
132 Provider* p = providers;
133 while (p) {
134 Provider* next = p->next;
135 if (!p->is_static)
136 delete p;
137 p = next;
138 }
139 }
initial.commitd7cae122008-07-26 21:49:38140};
[email protected]52a261f2009-03-03 15:01:12141
[email protected]6de0fd1d2011-11-15 13:31:49142static base::LazyInstance<PathData> g_path_data = LAZY_INSTANCE_INITIALIZER;
[email protected]625332e02010-12-14 07:48:49143
[email protected]1265917f2008-08-12 17:33:52144static PathData* GetPathData() {
[email protected]625332e02010-12-14 07:48:49145 return g_path_data.Pointer();
[email protected]1265917f2008-08-12 17:33:52146}
initial.commitd7cae122008-07-26 21:49:38147
[email protected]d6b3af92012-09-26 19:05:12148// Tries to find |key| in the cache. |path_data| should be locked by the caller!
149bool LockedGetFromCache(int key, const PathData* path_data, FilePath* result) {
[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
153 PathMap::const_iterator it = path_data->cache.find(key);
154 if (it != path_data->cache.end()) {
155 *result = it->second;
156 return true;
157 }
158 return false;
159}
160
[email protected]d6b3af92012-09-26 19:05:12161// Tries to find |key| in the overrides map. |path_data| should be locked by the
162// caller!
163bool LockedGetFromOverrides(int key, PathData* path_data, FilePath* result) {
[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);
[email protected]8e03fec2011-03-31 20:34:25185 DCHECK_GE(key, base::DIR_CURRENT);
initial.commitd7cae122008-07-26 21:49:38186
187 // special case the current directory because it can never be cached
[email protected]5af2edb92008-08-08 20:16:08188 if (key == base::DIR_CURRENT)
[email protected]1010f7d2008-08-06 16:29:44189 return file_util::GetCurrentDirectory(result);
initial.commitd7cae122008-07-26 21:49:38190
[email protected]d6b3af92012-09-26 19:05:12191 Provider* provider = NULL;
192 {
193 base::AutoLock scoped_lock(path_data->lock);
194 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.
220 if (!file_util::AbsolutePath(&path)) {
221 return false;
222 }
223 }
[email protected]640517f2008-10-30 23:54:04224 *result = path;
[email protected]d6b3af92012-09-26 19:05:12225
226 base::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
[email protected]d6b3af92012-09-26 19:05:12233// static
[email protected]eca6a4f2009-06-25 17:29:09234bool PathService::Override(int key, const FilePath& path) {
[email protected]cb571e752012-05-09 10:50:10235 // Just call the full function with true for the value of |create|.
236 return OverrideAndCreateIfNeeded(key, path, true);
237}
238
[email protected]d6b3af92012-09-26 19:05:12239// static
[email protected]cb571e752012-05-09 10:50:10240bool PathService::OverrideAndCreateIfNeeded(int key,
241 const FilePath& path,
242 bool create) {
[email protected]1265917f2008-08-12 17:33:52243 PathData* path_data = GetPathData();
initial.commitd7cae122008-07-26 21:49:38244 DCHECK(path_data);
[email protected]88563f62011-03-13 22:13:33245 DCHECK_GT(key, base::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
253 // this to the absolute path because on POSIX, AbsolutePath fails if called
254 // on a non-existent path.
255 if (!file_util::PathExists(file_path) &&
256 !file_util::CreateDirectory(file_path))
257 return false;
258 }
initial.commitd7cae122008-07-26 21:49:38259
[email protected]dabdb682009-10-27 23:31:36260 // We need to have an absolute path, as extensions and plugins don't like
[email protected]846c3ecea2011-12-14 18:47:26261 // relative paths, and will gladly crash the browser in CHECK()s if they get a
[email protected]dabdb682009-10-27 23:31:36262 // relative path.
263 if (!file_util::AbsolutePath(&file_path))
264 return false;
265
[email protected]20305ec2011-01-21 04:55:52266 base::AutoLock scoped_lock(path_data->lock);
[email protected]34e043b2010-09-09 23:49:04267
268 // Clear the cache now. Some of its entries could have depended
269 // on the value we are overriding, and are now out of sync with reality.
270 path_data->cache.clear();
271
[email protected]34e043b2010-09-09 23:49:04272 path_data->overrides[key] = file_path;
273
initial.commitd7cae122008-07-26 21:49:38274 return true;
275}
276
[email protected]d6b3af92012-09-26 19:05:12277// static
278bool PathService::RemoveOverride(int key) {
279 PathData* path_data = GetPathData();
280 DCHECK(path_data);
281
282 base::AutoLock scoped_lock(path_data->lock);
283
284 if (path_data->overrides.find(key) == path_data->overrides.end())
285 return false;
286
287 // Clear the cache now. Some of its entries could have depended on the value
288 // we are going to remove, and are now out of sync.
289 path_data->cache.clear();
290
291 path_data->overrides.erase(key);
292
293 return true;
294}
295
296// static
initial.commitd7cae122008-07-26 21:49:38297void PathService::RegisterProvider(ProviderFunc func, int key_start,
298 int key_end) {
[email protected]1265917f2008-08-12 17:33:52299 PathData* path_data = GetPathData();
initial.commitd7cae122008-07-26 21:49:38300 DCHECK(path_data);
[email protected]88563f62011-03-13 22:13:33301 DCHECK_GT(key_end, key_start);
initial.commitd7cae122008-07-26 21:49:38302
initial.commitd7cae122008-07-26 21:49:38303 Provider* p;
304
initial.commitd7cae122008-07-26 21:49:38305 p = new Provider;
[email protected]173cb8a02008-08-20 15:47:39306 p->is_static = false;
initial.commitd7cae122008-07-26 21:49:38307 p->func = func;
initial.commitd7cae122008-07-26 21:49:38308#ifndef NDEBUG
309 p->key_start = key_start;
310 p->key_end = key_end;
311#endif
[email protected]d6b3af92012-09-26 19:05:12312
313 base::AutoLock scoped_lock(path_data->lock);
314
315#ifndef NDEBUG
316 Provider *iter = path_data->providers;
317 while (iter) {
318 DCHECK(key_start >= iter->key_end || key_end <= iter->key_start) <<
319 "path provider collision";
320 iter = iter->next;
321 }
322#endif
323
324 p->next = path_data->providers;
initial.commitd7cae122008-07-26 21:49:38325 path_data->providers = p;
326}
[email protected]c5a726b32013-01-29 00:56:56327
328// static
329void PathService::DisableCache() {
330 PathData* path_data = GetPathData();
331 DCHECK(path_data);
332
333 base::AutoLock scoped_lock(path_data->lock);
334 path_data->cache.clear();
335 path_data->cache_disabled = true;
336}