Home | History | Annotate | Download | only in base
      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