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