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