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 126 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 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! 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! 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 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 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 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 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 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 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