• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 #include <unordered_map>
8 
9 #if defined(OS_WIN)
10 #include <windows.h>
11 #include <shellapi.h>
12 #include <shlobj.h>
13 #endif
14 
15 #include "base/files/file_path.h"
16 #include "base/files/file_util.h"
17 #include "base/logging.h"
18 #include "base/synchronization/lock.h"
19 #include "build/build_config.h"
20 
21 namespace base {
22 
23 bool PathProvider(int key, FilePath* result);
24 
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_FUCHSIA)
32 bool PathProviderFuchsia(int key, FilePath* result);
33 #elif defined(OS_POSIX)
34 // PathProviderPosix is the default path provider on POSIX OSes other than
35 // Mac and Android.
36 bool PathProviderPosix(int key, FilePath* result);
37 #endif
38 
39 namespace {
40 
41 typedef std::unordered_map<int, FilePath> PathMap;
42 
43 // We keep a linked list of providers.  In a debug build we ensure that no two
44 // providers claim overlapping keys.
45 struct Provider {
46   PathService::ProviderFunc func;
47   struct Provider* next;
48 #ifndef NDEBUG
49   int key_start;
50   int key_end;
51 #endif
52   bool is_static;
53 };
54 
55 Provider base_provider = {PathProvider, nullptr,
56 #ifndef NDEBUG
57                           PATH_START, PATH_END,
58 #endif
59                           true};
60 
61 #if defined(OS_WIN)
62 Provider base_provider_win = {
63   PathProviderWin,
64   &base_provider,
65 #ifndef NDEBUG
66   PATH_WIN_START,
67   PATH_WIN_END,
68 #endif
69   true
70 };
71 #endif
72 
73 #if defined(OS_MACOSX)
74 Provider base_provider_mac = {
75   PathProviderMac,
76   &base_provider,
77 #ifndef NDEBUG
78   PATH_MAC_START,
79   PATH_MAC_END,
80 #endif
81   true
82 };
83 #endif
84 
85 #if defined(OS_ANDROID)
86 Provider base_provider_android = {
87   PathProviderAndroid,
88   &base_provider,
89 #ifndef NDEBUG
90   PATH_ANDROID_START,
91   PATH_ANDROID_END,
92 #endif
93   true
94 };
95 #endif
96 
97 #if defined(OS_FUCHSIA)
98 Provider 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)
107 Provider base_provider_posix = {
108   PathProviderPosix,
109   &base_provider,
110 #ifndef NDEBUG
111   PATH_POSIX_START,
112   PATH_POSIX_END,
113 #endif
114   true
115 };
116 #endif
117 
118 
119 struct PathData {
120   Lock lock;
121   PathMap cache;        // Cache mappings from path key to path value.
122   PathMap overrides;    // Track path overrides.
123   Provider* providers;  // Linked list of path service providers.
124   bool cache_disabled;  // Don't use cache if true;
125 
PathDatabase::__anon62b218bb0111::PathData126   PathData() : cache_disabled(false) {
127 #if defined(OS_WIN)
128     providers = &base_provider_win;
129 #elif defined(OS_MACOSX)
130     providers = &base_provider_mac;
131 #elif defined(OS_ANDROID)
132     providers = &base_provider_android;
133 #elif defined(OS_FUCHSIA)
134     providers = &base_provider_fuchsia;
135 #elif defined(OS_POSIX)
136     providers = &base_provider_posix;
137 #endif
138   }
139 };
140 
GetPathData()141 static PathData* GetPathData() {
142   static auto* path_data = new PathData();
143   return path_data;
144 }
145 
146 // Tries to find |key| in the cache. |path_data| should be locked by the caller!
LockedGetFromCache(int key,const PathData * path_data,FilePath * result)147 bool LockedGetFromCache(int key, const PathData* path_data, FilePath* result) {
148   if (path_data->cache_disabled)
149     return false;
150   // 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 
159 // Tries to find |key| in the overrides map. |path_data| should be locked by the
160 // caller!
LockedGetFromOverrides(int key,PathData * path_data,FilePath * result)161 bool LockedGetFromOverrides(int key, PathData* path_data, FilePath* result) {
162   // check for an overridden version.
163   PathMap::const_iterator it = path_data->overrides.find(key);
164   if (it != path_data->overrides.end()) {
165     if (!path_data->cache_disabled)
166       path_data->cache[key] = it->second;
167     *result = it->second;
168     return true;
169   }
170   return false;
171 }
172 
173 }  // namespace
174 
175 // 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
Get(int key,FilePath * result)179 bool PathService::Get(int key, FilePath* result) {
180   PathData* path_data = GetPathData();
181   DCHECK(path_data);
182   DCHECK(result);
183   DCHECK_GE(key, DIR_CURRENT);
184 
185   // special case the current directory because it can never be cached
186   if (key == DIR_CURRENT)
187     return GetCurrentDirectory(result);
188 
189   Provider* provider = nullptr;
190   {
191     AutoLock scoped_lock(path_data->lock);
192     if (LockedGetFromCache(key, path_data, result))
193       return true;
194 
195     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   }
201 
202   FilePath path;
203 
204   // Iterating does not need the lock because only the list head might be
205   // modified on another thread.
206   while (provider) {
207     if (provider->func(key, &path))
208       break;
209     DCHECK(path.empty()) << "provider should not have modified path";
210     provider = provider->next;
211   }
212 
213   if (path.empty())
214     return false;
215 
216   if (path.ReferencesParent()) {
217     // Make sure path service never returns a path with ".." in it.
218     path = MakeAbsoluteFilePath(path);
219     if (path.empty())
220       return false;
221   }
222   *result = path;
223 
224   AutoLock scoped_lock(path_data->lock);
225   if (!path_data->cache_disabled)
226     path_data->cache[key] = path;
227 
228   return true;
229 }
230 
231 // static
Override(int key,const FilePath & path)232 bool PathService::Override(int key, const FilePath& path) {
233   // 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);
236 }
237 
238 // static
OverrideAndCreateIfNeeded(int key,const FilePath & path,bool is_absolute,bool create)239 bool PathService::OverrideAndCreateIfNeeded(int key,
240                                             const FilePath& path,
241                                             bool is_absolute,
242                                             bool create) {
243   PathData* path_data = GetPathData();
244   DCHECK(path_data);
245   DCHECK_GT(key, DIR_CURRENT) << "invalid path key";
246 
247   FilePath file_path = path;
248 
249   // 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, MakeAbsoluteFilePath fails
254     // if called on a non-existent path.
255     if (!PathExists(file_path) && !CreateDirectory(file_path))
256       return false;
257   }
258 
259   // We need to have an absolute path.
260   if (!is_absolute) {
261     file_path = MakeAbsoluteFilePath(file_path);
262     if (file_path.empty())
263       return false;
264   }
265   DCHECK(file_path.IsAbsolute());
266 
267   AutoLock scoped_lock(path_data->lock);
268 
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 
273   path_data->overrides[key] = file_path;
274 
275   return true;
276 }
277 
278 // static
RemoveOverride(int key)279 bool PathService::RemoveOverride(int key) {
280   PathData* path_data = GetPathData();
281   DCHECK(path_data);
282 
283   AutoLock scoped_lock(path_data->lock);
284 
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
RegisterProvider(ProviderFunc func,int key_start,int key_end)298 void PathService::RegisterProvider(ProviderFunc func, int key_start,
299                                    int key_end) {
300   PathData* path_data = GetPathData();
301   DCHECK(path_data);
302   DCHECK_GT(key_end, key_start);
303 
304   Provider* p;
305 
306   p = new Provider;
307   p->is_static = false;
308   p->func = func;
309 #ifndef NDEBUG
310   p->key_start = key_start;
311   p->key_end = key_end;
312 #endif
313 
314   AutoLock scoped_lock(path_data->lock);
315 
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;
326   path_data->providers = p;
327 }
328 
329 // static
DisableCache()330 void PathService::DisableCache() {
331   PathData* path_data = GetPathData();
332   DCHECK(path_data);
333 
334   AutoLock scoped_lock(path_data->lock);
335   path_data->cache.clear();
336   path_data->cache_disabled = true;
337 }
338 
339 }  // namespace base
340