Home | History | Annotate | Download | only in base
      1 // Copyright (c) 2011 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 #ifdef OS_WIN
      8 #include <windows.h>
      9 #include <shellapi.h>
     10 #include <shlobj.h>
     11 #endif
     12 
     13 #include "base/file_path.h"
     14 #include "base/file_util.h"
     15 #include "base/hash_tables.h"
     16 #include "base/lazy_instance.h"
     17 #include "base/logging.h"
     18 #include "base/synchronization/lock.h"
     19 
     20 namespace base {
     21   bool PathProvider(int key, FilePath* result);
     22 #if defined(OS_WIN)
     23   bool PathProviderWin(int key, FilePath* result);
     24 #elif defined(OS_MACOSX)
     25   bool PathProviderMac(int key, FilePath* result);
     26 #elif defined(OS_POSIX)
     27   bool PathProviderPosix(int key, FilePath* result);
     28 #endif
     29 }
     30 
     31 namespace {
     32 
     33 typedef base::hash_map<int, FilePath> PathMap;
     34 
     35 // We keep a linked list of providers.  In a debug build we ensure that no two
     36 // providers claim overlapping keys.
     37 struct Provider {
     38   PathService::ProviderFunc func;
     39   struct Provider* next;
     40 #ifndef NDEBUG
     41   int key_start;
     42   int key_end;
     43 #endif
     44   bool is_static;
     45 };
     46 
     47 static Provider base_provider = {
     48   base::PathProvider,
     49   NULL,
     50 #ifndef NDEBUG
     51   base::PATH_START,
     52   base::PATH_END,
     53 #endif
     54   true
     55 };
     56 
     57 #if defined(OS_WIN)
     58 static Provider base_provider_win = {
     59   base::PathProviderWin,
     60   &base_provider,
     61 #ifndef NDEBUG
     62   base::PATH_WIN_START,
     63   base::PATH_WIN_END,
     64 #endif
     65   true
     66 };
     67 #endif
     68 
     69 #if defined(OS_MACOSX)
     70 static Provider base_provider_mac = {
     71   base::PathProviderMac,
     72   &base_provider,
     73 #ifndef NDEBUG
     74   base::PATH_MAC_START,
     75   base::PATH_MAC_END,
     76 #endif
     77   true
     78 };
     79 #endif
     80 
     81 #if defined(OS_POSIX) && !defined(OS_MACOSX)
     82 static Provider base_provider_posix = {
     83   base::PathProviderPosix,
     84   &base_provider,
     85 #ifndef NDEBUG
     86   0,
     87   0,
     88 #endif
     89   true
     90 };
     91 #endif
     92 
     93 
     94 struct PathData {
     95   base::Lock lock;
     96   PathMap cache;        // Cache mappings from path key to path value.
     97   PathMap overrides;    // Track path overrides.
     98   Provider* providers;  // Linked list of path service providers.
     99 
    100   PathData() {
    101 #if defined(OS_WIN)
    102     providers = &base_provider_win;
    103 #elif defined(OS_MACOSX)
    104     providers = &base_provider_mac;
    105 #elif defined(OS_POSIX)
    106     providers = &base_provider_posix;
    107 #endif
    108   }
    109 
    110   ~PathData() {
    111     Provider* p = providers;
    112     while (p) {
    113       Provider* next = p->next;
    114       if (!p->is_static)
    115         delete p;
    116       p = next;
    117     }
    118   }
    119 };
    120 
    121 static base::LazyInstance<PathData> g_path_data(base::LINKER_INITIALIZED);
    122 
    123 static PathData* GetPathData() {
    124   return g_path_data.Pointer();
    125 }
    126 
    127 }  // namespace
    128 
    129 
    130 // static
    131 bool PathService::GetFromCache(int key, FilePath* result) {
    132   PathData* path_data = GetPathData();
    133   base::AutoLock scoped_lock(path_data->lock);
    134 
    135   // check for a cached version
    136   PathMap::const_iterator it = path_data->cache.find(key);
    137   if (it != path_data->cache.end()) {
    138     *result = it->second;
    139     return true;
    140   }
    141   return false;
    142 }
    143 
    144 // static
    145 bool PathService::GetFromOverrides(int key, FilePath* result) {
    146   PathData* path_data = GetPathData();
    147   base::AutoLock scoped_lock(path_data->lock);
    148 
    149   // check for an overriden version.
    150   PathMap::const_iterator it = path_data->overrides.find(key);
    151   if (it != path_data->overrides.end()) {
    152     *result = it->second;
    153     return true;
    154   }
    155   return false;
    156 }
    157 
    158 // static
    159 void PathService::AddToCache(int key, const FilePath& path) {
    160   PathData* path_data = GetPathData();
    161   base::AutoLock scoped_lock(path_data->lock);
    162   // Save the computed path in our cache.
    163   path_data->cache[key] = path;
    164 }
    165 
    166 // TODO(brettw): this function does not handle long paths (filename > MAX_PATH)
    167 // characters). This isn't supported very well by Windows right now, so it is
    168 // moot, but we should keep this in mind for the future.
    169 // static
    170 bool PathService::Get(int key, FilePath* result) {
    171   PathData* path_data = GetPathData();
    172   DCHECK(path_data);
    173   DCHECK(result);
    174   DCHECK_GE(key, base::DIR_CURRENT);
    175 
    176   // special case the current directory because it can never be cached
    177   if (key == base::DIR_CURRENT)
    178     return file_util::GetCurrentDirectory(result);
    179 
    180   if (GetFromCache(key, result))
    181     return true;
    182 
    183   if (GetFromOverrides(key, result))
    184     return true;
    185 
    186   FilePath path;
    187 
    188   // search providers for the requested path
    189   // NOTE: it should be safe to iterate here without the lock
    190   // since RegisterProvider always prepends.
    191   Provider* provider = path_data->providers;
    192   while (provider) {
    193     if (provider->func(key, &path))
    194       break;
    195     DCHECK(path.empty()) << "provider should not have modified path";
    196     provider = provider->next;
    197   }
    198 
    199   if (path.empty())
    200     return false;
    201 
    202   AddToCache(key, path);
    203 
    204   *result = path;
    205   return true;
    206 }
    207 
    208 bool PathService::Override(int key, const FilePath& path) {
    209   PathData* path_data = GetPathData();
    210   DCHECK(path_data);
    211   DCHECK_GT(key, base::DIR_CURRENT) << "invalid path key";
    212 
    213   FilePath file_path = path;
    214 
    215   // Make sure the directory exists. We need to do this before we translate
    216   // this to the absolute path because on POSIX, AbsolutePath fails if called
    217   // on a non-existant path.
    218   if (!file_util::PathExists(file_path) &&
    219       !file_util::CreateDirectory(file_path))
    220     return false;
    221 
    222   // We need to have an absolute path, as extensions and plugins don't like
    223   // relative paths, and will glady crash the browser in CHECK()s if they get a
    224   // relative path.
    225   if (!file_util::AbsolutePath(&file_path))
    226     return false;
    227 
    228   base::AutoLock scoped_lock(path_data->lock);
    229 
    230   // Clear the cache now. Some of its entries could have depended
    231   // on the value we are overriding, and are now out of sync with reality.
    232   path_data->cache.clear();
    233 
    234   path_data->cache[key] = file_path;
    235   path_data->overrides[key] = file_path;
    236 
    237   return true;
    238 }
    239 
    240 void PathService::RegisterProvider(ProviderFunc func, int key_start,
    241                                    int key_end) {
    242   PathData* path_data = GetPathData();
    243   DCHECK(path_data);
    244   DCHECK_GT(key_end, key_start);
    245 
    246   base::AutoLock scoped_lock(path_data->lock);
    247 
    248   Provider* p;
    249 
    250 #ifndef NDEBUG
    251   p = path_data->providers;
    252   while (p) {
    253     DCHECK(key_start >= p->key_end || key_end <= p->key_start) <<
    254       "path provider collision";
    255     p = p->next;
    256   }
    257 #endif
    258 
    259   p = new Provider;
    260   p->is_static = false;
    261   p->func = func;
    262   p->next = path_data->providers;
    263 #ifndef NDEBUG
    264   p->key_start = key_start;
    265   p->key_end = key_end;
    266 #endif
    267   path_data->providers = p;
    268 }
    269