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
20using base::FilePath;
21using base::MakeAbsoluteFilePath;
22
23namespace base {
24  bool PathProvider(int key, FilePath* result);
25#if defined(OS_WIN)
26  bool PathProviderWin(int key, FilePath* result);
27#elif defined(OS_MACOSX)
28  bool PathProviderMac(int key, FilePath* result);
29#elif defined(OS_ANDROID)
30  bool 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.
34  bool PathProviderPosix(int key, FilePath* result);
35#endif
36}
37
38namespace {
39
40typedef base::hash_map<int, FilePath> PathMap;
41
42// We keep a linked list of providers.  In a debug build we ensure that no two
43// providers claim overlapping keys.
44struct Provider {
45  PathService::ProviderFunc func;
46  struct Provider* next;
47#ifndef NDEBUG
48  int key_start;
49  int key_end;
50#endif
51  bool is_static;
52};
53
54Provider base_provider = {
55  base::PathProvider,
56  NULL,
57#ifndef NDEBUG
58  base::PATH_START,
59  base::PATH_END,
60#endif
61  true
62};
63
64#if defined(OS_WIN)
65Provider base_provider_win = {
66  base::PathProviderWin,
67  &base_provider,
68#ifndef NDEBUG
69  base::PATH_WIN_START,
70  base::PATH_WIN_END,
71#endif
72  true
73};
74#endif
75
76#if defined(OS_MACOSX)
77Provider base_provider_mac = {
78  base::PathProviderMac,
79  &base_provider,
80#ifndef NDEBUG
81  base::PATH_MAC_START,
82  base::PATH_MAC_END,
83#endif
84  true
85};
86#endif
87
88#if defined(OS_ANDROID)
89Provider base_provider_android = {
90  base::PathProviderAndroid,
91  &base_provider,
92#ifndef NDEBUG
93  base::PATH_ANDROID_START,
94  base::PATH_ANDROID_END,
95#endif
96  true
97};
98#endif
99
100#if defined(OS_POSIX) && !defined(OS_MACOSX) && !defined(OS_ANDROID)
101Provider base_provider_posix = {
102  base::PathProviderPosix,
103  &base_provider,
104#ifndef NDEBUG
105  base::PATH_POSIX_START,
106  base::PATH_POSIX_END,
107#endif
108  true
109};
110#endif
111
112
113struct PathData {
114  base::Lock lock;
115  PathMap cache;        // Cache mappings from path key to path value.
116  PathMap overrides;    // Track path overrides.
117  Provider* providers;  // Linked list of path service providers.
118  bool cache_disabled;  // Don't use cache if true;
119
120  PathData() : cache_disabled(false) {
121#if defined(OS_WIN)
122    providers = &base_provider_win;
123#elif defined(OS_MACOSX)
124    providers = &base_provider_mac;
125#elif defined(OS_ANDROID)
126    providers = &base_provider_android;
127#elif defined(OS_POSIX)
128    providers = &base_provider_posix;
129#endif
130  }
131
132  ~PathData() {
133    Provider* p = providers;
134    while (p) {
135      Provider* next = p->next;
136      if (!p->is_static)
137        delete p;
138      p = next;
139    }
140  }
141};
142
143static base::LazyInstance<PathData> g_path_data = LAZY_INSTANCE_INITIALIZER;
144
145static PathData* GetPathData() {
146  return g_path_data.Pointer();
147}
148
149// Tries to find |key| in the cache. |path_data| should be locked by the caller!
150bool LockedGetFromCache(int key, const PathData* path_data, FilePath* result) {
151  if (path_data->cache_disabled)
152    return false;
153  // check for a cached version
154  PathMap::const_iterator it = path_data->cache.find(key);
155  if (it != path_data->cache.end()) {
156    *result = it->second;
157    return true;
158  }
159  return false;
160}
161
162// Tries to find |key| in the overrides map. |path_data| should be locked by the
163// caller!
164bool LockedGetFromOverrides(int key, PathData* path_data, FilePath* result) {
165  // check for an overridden version.
166  PathMap::const_iterator it = path_data->overrides.find(key);
167  if (it != path_data->overrides.end()) {
168    if (!path_data->cache_disabled)
169      path_data->cache[key] = it->second;
170    *result = it->second;
171    return true;
172  }
173  return false;
174}
175
176}  // namespace
177
178// TODO(brettw): this function does not handle long paths (filename > MAX_PATH)
179// characters). This isn't supported very well by Windows right now, so it is
180// moot, but we should keep this in mind for the future.
181// static
182bool PathService::Get(int key, FilePath* result) {
183  PathData* path_data = GetPathData();
184  DCHECK(path_data);
185  DCHECK(result);
186  DCHECK_GE(key, base::DIR_CURRENT);
187
188  // special case the current directory because it can never be cached
189  if (key == base::DIR_CURRENT)
190    return base::GetCurrentDirectory(result);
191
192  Provider* provider = NULL;
193  {
194    base::AutoLock scoped_lock(path_data->lock);
195    if (LockedGetFromCache(key, path_data, result))
196      return true;
197
198    if (LockedGetFromOverrides(key, path_data, result))
199      return true;
200
201    // Get the beginning of the list while it is still locked.
202    provider = path_data->providers;
203  }
204
205  FilePath path;
206
207  // Iterating does not need the lock because only the list head might be
208  // modified on another thread.
209  while (provider) {
210    if (provider->func(key, &path))
211      break;
212    DCHECK(path.empty()) << "provider should not have modified path";
213    provider = provider->next;
214  }
215
216  if (path.empty())
217    return false;
218
219  if (path.ReferencesParent()) {
220    // Make sure path service never returns a path with ".." in it.
221    path = MakeAbsoluteFilePath(path);
222    if (path.empty())
223      return false;
224  }
225  *result = path;
226
227  base::AutoLock scoped_lock(path_data->lock);
228  if (!path_data->cache_disabled)
229    path_data->cache[key] = path;
230
231  return true;
232}
233
234// static
235bool PathService::Override(int key, const FilePath& path) {
236  // Just call the full function with true for the value of |create|, and
237  // assume that |path| may not be absolute yet.
238  return OverrideAndCreateIfNeeded(key, path, false, true);
239}
240
241// static
242bool PathService::OverrideAndCreateIfNeeded(int key,
243                                            const FilePath& path,
244                                            bool is_absolute,
245                                            bool create) {
246  PathData* path_data = GetPathData();
247  DCHECK(path_data);
248  DCHECK_GT(key, base::DIR_CURRENT) << "invalid path key";
249
250  FilePath file_path = path;
251
252  // For some locations this will fail if called from inside the sandbox there-
253  // fore we protect this call with a flag.
254  if (create) {
255    // Make sure the directory exists. We need to do this before we translate
256    // this to the absolute path because on POSIX, MakeAbsoluteFilePath fails
257    // if called on a non-existent path.
258    if (!base::PathExists(file_path) &&
259        !base::CreateDirectory(file_path))
260      return false;
261  }
262
263  // We need to have an absolute path.
264  if (!is_absolute) {
265    file_path = MakeAbsoluteFilePath(file_path);
266    if (file_path.empty())
267      return false;
268  }
269  DCHECK(file_path.IsAbsolute());
270
271  base::AutoLock scoped_lock(path_data->lock);
272
273  // Clear the cache now. Some of its entries could have depended
274  // on the value we are overriding, and are now out of sync with reality.
275  path_data->cache.clear();
276
277  path_data->overrides[key] = file_path;
278
279  return true;
280}
281
282// static
283bool PathService::RemoveOverride(int key) {
284  PathData* path_data = GetPathData();
285  DCHECK(path_data);
286
287  base::AutoLock scoped_lock(path_data->lock);
288
289  if (path_data->overrides.find(key) == path_data->overrides.end())
290    return false;
291
292  // Clear the cache now. Some of its entries could have depended on the value
293  // we are going to remove, and are now out of sync.
294  path_data->cache.clear();
295
296  path_data->overrides.erase(key);
297
298  return true;
299}
300
301// static
302void PathService::RegisterProvider(ProviderFunc func, int key_start,
303                                   int key_end) {
304  PathData* path_data = GetPathData();
305  DCHECK(path_data);
306  DCHECK_GT(key_end, key_start);
307
308  Provider* p;
309
310  p = new Provider;
311  p->is_static = false;
312  p->func = func;
313#ifndef NDEBUG
314  p->key_start = key_start;
315  p->key_end = key_end;
316#endif
317
318  base::AutoLock scoped_lock(path_data->lock);
319
320#ifndef NDEBUG
321  Provider *iter = path_data->providers;
322  while (iter) {
323    DCHECK(key_start >= iter->key_end || key_end <= iter->key_start) <<
324      "path provider collision";
325    iter = iter->next;
326  }
327#endif
328
329  p->next = path_data->providers;
330  path_data->providers = p;
331}
332
333// static
334void PathService::DisableCache() {
335  PathData* path_data = GetPathData();
336  DCHECK(path_data);
337
338  base::AutoLock scoped_lock(path_data->lock);
339  path_data->cache.clear();
340  path_data->cache_disabled = true;
341}
342