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 20namespace 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 31namespace { 32 33typedef 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. 37struct 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 47static 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) 58static 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) 70static 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) 82static 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 94struct 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 121static base::LazyInstance<PathData> g_path_data(base::LINKER_INITIALIZED); 122 123static PathData* GetPathData() { 124 return g_path_data.Pointer(); 125} 126 127} // namespace 128 129 130// static 131bool 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 145bool 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 159void 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 170bool 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 208bool 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 240void 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