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