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 #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 
    118   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 
    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!
    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!
    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
    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
    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
    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
    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
    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
    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