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