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