path_service.cc revision 21a249e4d9cb0b2ec6f0ff84ed5f7939ea67ac52
1// 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"
16#include "base/lazy_instance.h"
17#include "base/logging.h"
18#include "base/synchronization/lock.h"
19#include "build/build_config.h"
20
21namespace base {
22
23bool PathProvider(int key, FilePath* result);
24
25#if defined(OS_WIN)
26bool PathProviderWin(int key, FilePath* result);
27#elif defined(OS_MACOSX)
28bool PathProviderMac(int key, FilePath* result);
29#elif defined(OS_ANDROID)
30bool PathProviderAndroid(int key, FilePath* result);
31#elif defined(OS_POSIX)
32// PathProviderPosix is the default path provider on POSIX OSes other than
33// Mac and Android.
34bool PathProviderPosix(int key, FilePath* result);
35#endif
36
37namespace {
38
39typedef hash_map<int, FilePath> PathMap;
40
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
50  bool is_static;
51};
52
53Provider base_provider = {
54  PathProvider,
55  NULL,
56#ifndef NDEBUG
57  PATH_START,
58  PATH_END,
59#endif
60  true
61};
62
63#if defined(OS_WIN)
64Provider base_provider_win = {
65  PathProviderWin,
66  &base_provider,
67#ifndef NDEBUG
68  PATH_WIN_START,
69  PATH_WIN_END,
70#endif
71  true
72};
73#endif
74
75#if defined(OS_MACOSX)
76Provider base_provider_mac = {
77  PathProviderMac,
78  &base_provider,
79#ifndef NDEBUG
80  PATH_MAC_START,
81  PATH_MAC_END,
82#endif
83  true
84};
85#endif
86
87#if defined(OS_ANDROID)
88Provider base_provider_android = {
89  PathProviderAndroid,
90  &base_provider,
91#ifndef NDEBUG
92  PATH_ANDROID_START,
93  PATH_ANDROID_END,
94#endif
95  true
96};
97#endif
98
99#if defined(OS_POSIX) && !defined(OS_MACOSX) && !defined(OS_ANDROID)
100Provider base_provider_posix = {
101  PathProviderPosix,
102  &base_provider,
103#ifndef NDEBUG
104  PATH_POSIX_START,
105  PATH_POSIX_END,
106#endif
107  true
108};
109#endif
110
111
112struct PathData {
113  Lock lock;
114  PathMap cache;        // Cache mappings from path key to path value.
115  PathMap overrides;    // Track path overrides.
116  Provider* providers;  // Linked list of path service providers.
117  bool cache_disabled;  // Don't use cache if true;
118
119  PathData() : cache_disabled(false) {
120#if defined(OS_WIN)
121    providers = &base_provider_win;
122#elif defined(OS_MACOSX)
123    providers = &base_provider_mac;
124#elif defined(OS_ANDROID)
125    providers = &base_provider_android;
126#elif defined(OS_POSIX)
127    providers = &base_provider_posix;
128#endif
129  }
130};
131
132static LazyInstance<PathData>::Leaky g_path_data = LAZY_INSTANCE_INITIALIZER;
133
134static PathData* GetPathData() {
135  return g_path_data.Pointer();
136}
137
138// 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) {
140  if (path_data->cache_disabled)
141    return false;
142  // 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
151// 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) {
154  // check for an overridden version.
155  PathMap::const_iterator it = path_data->overrides.find(key);
156  if (it != path_data->overrides.end()) {
157    if (!path_data->cache_disabled)
158      path_data->cache[key] = it->second;
159    *result = it->second;
160    return true;
161  }
162  return false;
163}
164
165}  // namespace
166
167// 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
171bool PathService::Get(int key, FilePath* result) {
172  PathData* path_data = GetPathData();
173  DCHECK(path_data);
174  DCHECK(result);
175  DCHECK_GE(key, DIR_CURRENT);
176
177  // special case the current directory because it can never be cached
178  if (key == DIR_CURRENT)
179    return GetCurrentDirectory(result);
180
181  Provider* provider = NULL;
182  {
183    AutoLock scoped_lock(path_data->lock);
184    if (LockedGetFromCache(key, path_data, result))
185      return true;
186
187    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  }
193
194  FilePath path;
195
196  // Iterating does not need the lock because only the list head might be
197  // modified on another thread.
198  while (provider) {
199    if (provider->func(key, &path))
200      break;
201    DCHECK(path.empty()) << "provider should not have modified path";
202    provider = provider->next;
203  }
204
205  if (path.empty())
206    return false;
207
208  if (path.ReferencesParent()) {
209    // Make sure path service never returns a path with ".." in it.
210    path = MakeAbsoluteFilePath(path);
211    if (path.empty())
212      return false;
213  }
214  *result = path;
215
216  AutoLock scoped_lock(path_data->lock);
217  if (!path_data->cache_disabled)
218    path_data->cache[key] = path;
219
220  return true;
221}
222
223// static
224bool PathService::Override(int key, const FilePath& path) {
225  // 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);
228}
229
230// static
231bool PathService::OverrideAndCreateIfNeeded(int key,
232                                            const FilePath& path,
233                                            bool is_absolute,
234                                            bool create) {
235  PathData* path_data = GetPathData();
236  DCHECK(path_data);
237  DCHECK_GT(key, DIR_CURRENT) << "invalid path key";
238
239  FilePath file_path = path;
240
241  // 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
245    // this to the absolute path because on POSIX, MakeAbsoluteFilePath fails
246    // if called on a non-existent path.
247    if (!PathExists(file_path) && !CreateDirectory(file_path))
248      return false;
249  }
250
251  // We need to have an absolute path.
252  if (!is_absolute) {
253    file_path = MakeAbsoluteFilePath(file_path);
254    if (file_path.empty())
255      return false;
256  }
257  DCHECK(file_path.IsAbsolute());
258
259  AutoLock scoped_lock(path_data->lock);
260
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
265  path_data->overrides[key] = file_path;
266
267  return true;
268}
269
270// static
271bool PathService::RemoveOverride(int key) {
272  PathData* path_data = GetPathData();
273  DCHECK(path_data);
274
275  AutoLock scoped_lock(path_data->lock);
276
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
290void PathService::RegisterProvider(ProviderFunc func, int key_start,
291                                   int key_end) {
292  PathData* path_data = GetPathData();
293  DCHECK(path_data);
294  DCHECK_GT(key_end, key_start);
295
296  Provider* p;
297
298  p = new Provider;
299  p->is_static = false;
300  p->func = func;
301#ifndef NDEBUG
302  p->key_start = key_start;
303  p->key_end = key_end;
304#endif
305
306  AutoLock scoped_lock(path_data->lock);
307
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;
318  path_data->providers = p;
319}
320
321// static
322void PathService::DisableCache() {
323  PathData* path_data = GetPathData();
324  DCHECK(path_data);
325
326  AutoLock scoped_lock(path_data->lock);
327  path_data->cache.clear();
328  path_data->cache_disabled = true;
329}
330
331}  // namespace base
332