service.cc revision 46d4c2bc3267f3f028f39e7e311b0f89aba2e4fd
1// Copyright 2014 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 "chrome/browser/chromeos/file_system_provider/service.h" 6 7#include "base/files/file_path.h" 8#include "base/prefs/pref_service.h" 9#include "base/prefs/scoped_user_pref_update.h" 10#include "base/stl_util.h" 11#include "chrome/browser/chromeos/file_system_provider/mount_path_util.h" 12#include "chrome/browser/chromeos/file_system_provider/observer.h" 13#include "chrome/browser/chromeos/file_system_provider/provided_file_system.h" 14#include "chrome/browser/chromeos/file_system_provider/provided_file_system_info.h" 15#include "chrome/browser/chromeos/file_system_provider/provided_file_system_interface.h" 16#include "chrome/browser/chromeos/file_system_provider/service_factory.h" 17#include "chrome/common/pref_names.h" 18#include "components/pref_registry/pref_registry_syncable.h" 19#include "extensions/browser/event_router.h" 20#include "extensions/browser/extension_registry.h" 21#include "extensions/browser/extension_system.h" 22#include "webkit/browser/fileapi/external_mount_points.h" 23 24namespace chromeos { 25namespace file_system_provider { 26namespace { 27 28// Maximum number of file systems to be mounted in the same time, per profile. 29const size_t kMaxFileSystems = 16; 30 31// Default factory for provided file systems. The |event_router| must not be 32// NULL. 33ProvidedFileSystemInterface* CreateProvidedFileSystem( 34 extensions::EventRouter* event_router, 35 const ProvidedFileSystemInfo& file_system_info) { 36 DCHECK(event_router); 37 return new ProvidedFileSystem(event_router, file_system_info); 38} 39 40} // namespace 41 42const char kPrefKeyFileSystemId[] = "file-system-id"; 43const char kPrefKeyFileSystemName[] = "file-system-name"; 44 45void RegisterProfilePrefs(user_prefs::PrefRegistrySyncable* registry) { 46 registry->RegisterDictionaryPref( 47 prefs::kFileSystemProviderMounted, 48 user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF); 49} 50 51Service::Service(Profile* profile, 52 extensions::ExtensionRegistry* extension_registry) 53 : profile_(profile), 54 extension_registry_(extension_registry), 55 file_system_factory_(base::Bind(CreateProvidedFileSystem)), 56 weak_ptr_factory_(this) { 57 extension_registry_->AddObserver(this); 58} 59 60Service::~Service() { 61 extension_registry_->RemoveObserver(this); 62 RememberFileSystems(); 63 64 ProvidedFileSystemMap::iterator it = file_system_map_.begin(); 65 while (it != file_system_map_.end()) { 66 const std::string file_system_id = 67 it->second->GetFileSystemInfo().file_system_id(); 68 const std::string extension_id = 69 it->second->GetFileSystemInfo().extension_id(); 70 ++it; 71 UnmountFileSystem(extension_id, file_system_id); 72 } 73 74 DCHECK_EQ(0u, file_system_map_.size()); 75 STLDeleteValues(&file_system_map_); 76} 77 78// static 79Service* Service::Get(content::BrowserContext* context) { 80 return ServiceFactory::Get(context); 81} 82 83void Service::AddObserver(Observer* observer) { 84 DCHECK(observer); 85 observers_.AddObserver(observer); 86} 87 88void Service::RemoveObserver(Observer* observer) { 89 DCHECK(observer); 90 observers_.RemoveObserver(observer); 91} 92 93void Service::SetFileSystemFactoryForTests( 94 const FileSystemFactoryCallback& factory_callback) { 95 DCHECK(!factory_callback.is_null()); 96 file_system_factory_ = factory_callback; 97} 98 99bool Service::MountFileSystem(const std::string& extension_id, 100 const std::string& file_system_id, 101 const std::string& file_system_name) { 102 DCHECK(thread_checker_.CalledOnValidThread()); 103 104 // If already exists a file system provided by the same extension with this 105 // id, then abort. 106 if (GetProvidedFileSystem(extension_id, file_system_id)) { 107 FOR_EACH_OBSERVER(Observer, 108 observers_, 109 OnProvidedFileSystemMount(ProvidedFileSystemInfo(), 110 base::File::FILE_ERROR_EXISTS)); 111 return false; 112 } 113 114 // Restrict number of file systems to prevent system abusing. 115 if (file_system_map_.size() + 1 > kMaxFileSystems) { 116 FOR_EACH_OBSERVER( 117 Observer, 118 observers_, 119 OnProvidedFileSystemMount(ProvidedFileSystemInfo(), 120 base::File::FILE_ERROR_TOO_MANY_OPENED)); 121 return false; 122 } 123 124 fileapi::ExternalMountPoints* const mount_points = 125 fileapi::ExternalMountPoints::GetSystemInstance(); 126 DCHECK(mount_points); 127 128 // The mount point path and name are unique per system, since they are system 129 // wide. This is necessary for copying between profiles. 130 const base::FilePath& mount_path = 131 util::GetMountPath(profile_, extension_id, file_system_id); 132 const std::string mount_point_name = mount_path.BaseName().AsUTF8Unsafe(); 133 134 if (!mount_points->RegisterFileSystem(mount_point_name, 135 fileapi::kFileSystemTypeProvided, 136 fileapi::FileSystemMountOption(), 137 mount_path)) { 138 FOR_EACH_OBSERVER( 139 Observer, 140 observers_, 141 OnProvidedFileSystemMount(ProvidedFileSystemInfo(), 142 base::File::FILE_ERROR_INVALID_OPERATION)); 143 return false; 144 } 145 146 // Store the file system descriptor. Use the mount point name as the file 147 // system provider file system id. 148 // Examples: 149 // file_system_id = 41 150 // mount_point_name = b33f1337-41-5aa5 151 // mount_path = /provided/b33f1337-41-5aa5 152 ProvidedFileSystemInfo file_system_info( 153 extension_id, file_system_id, file_system_name, mount_path); 154 155 // The event router may be NULL for unit tests. 156 extensions::EventRouter* router = extensions::EventRouter::Get(profile_); 157 158 ProvidedFileSystemInterface* file_system = 159 file_system_factory_.Run(router, file_system_info); 160 DCHECK(file_system); 161 file_system_map_[FileSystemKey(extension_id, file_system_id)] = file_system; 162 mount_point_name_to_key_map_[mount_point_name] = 163 FileSystemKey(extension_id, file_system_id); 164 165 FOR_EACH_OBSERVER( 166 Observer, 167 observers_, 168 OnProvidedFileSystemMount(file_system_info, base::File::FILE_OK)); 169 170 return true; 171} 172 173bool Service::UnmountFileSystem(const std::string& extension_id, 174 const std::string& file_system_id) { 175 DCHECK(thread_checker_.CalledOnValidThread()); 176 177 const ProvidedFileSystemMap::iterator file_system_it = 178 file_system_map_.find(FileSystemKey(extension_id, file_system_id)); 179 if (file_system_it == file_system_map_.end()) { 180 const ProvidedFileSystemInfo empty_file_system_info; 181 FOR_EACH_OBSERVER( 182 Observer, 183 observers_, 184 OnProvidedFileSystemUnmount(empty_file_system_info, 185 base::File::FILE_ERROR_NOT_FOUND)); 186 return false; 187 } 188 189 fileapi::ExternalMountPoints* const mount_points = 190 fileapi::ExternalMountPoints::GetSystemInstance(); 191 DCHECK(mount_points); 192 193 const ProvidedFileSystemInfo& file_system_info = 194 file_system_it->second->GetFileSystemInfo(); 195 196 const std::string mount_point_name = 197 file_system_info.mount_path().BaseName().value(); 198 if (!mount_points->RevokeFileSystem(mount_point_name)) { 199 FOR_EACH_OBSERVER( 200 Observer, 201 observers_, 202 OnProvidedFileSystemUnmount(file_system_info, 203 base::File::FILE_ERROR_INVALID_OPERATION)); 204 return false; 205 } 206 207 FOR_EACH_OBSERVER( 208 Observer, 209 observers_, 210 OnProvidedFileSystemUnmount(file_system_info, base::File::FILE_OK)); 211 212 mount_point_name_to_key_map_.erase(mount_point_name); 213 214 delete file_system_it->second; 215 file_system_map_.erase(file_system_it); 216 217 return true; 218} 219 220bool Service::RequestUnmount(const std::string& extension_id, 221 const std::string& file_system_id) { 222 DCHECK(thread_checker_.CalledOnValidThread()); 223 224 ProvidedFileSystemMap::iterator file_system_it = 225 file_system_map_.find(FileSystemKey(extension_id, file_system_id)); 226 if (file_system_it == file_system_map_.end()) 227 return false; 228 229 file_system_it->second->RequestUnmount( 230 base::Bind(&Service::OnRequestUnmountStatus, 231 weak_ptr_factory_.GetWeakPtr(), 232 file_system_it->second->GetFileSystemInfo())); 233 return true; 234} 235 236std::vector<ProvidedFileSystemInfo> Service::GetProvidedFileSystemInfoList() { 237 DCHECK(thread_checker_.CalledOnValidThread()); 238 239 std::vector<ProvidedFileSystemInfo> result; 240 for (ProvidedFileSystemMap::const_iterator it = file_system_map_.begin(); 241 it != file_system_map_.end(); 242 ++it) { 243 result.push_back(it->second->GetFileSystemInfo()); 244 } 245 return result; 246} 247 248ProvidedFileSystemInterface* Service::GetProvidedFileSystem( 249 const std::string& extension_id, 250 const std::string& file_system_id) { 251 DCHECK(thread_checker_.CalledOnValidThread()); 252 253 const ProvidedFileSystemMap::const_iterator file_system_it = 254 file_system_map_.find(FileSystemKey(extension_id, file_system_id)); 255 if (file_system_it == file_system_map_.end()) 256 return NULL; 257 258 return file_system_it->second; 259} 260 261void Service::OnExtensionUnloaded( 262 content::BrowserContext* browser_context, 263 const extensions::Extension* extension, 264 extensions::UnloadedExtensionInfo::Reason reason) { 265 // If the reason is not a profile shutdown, then forget the mounted file 266 // systems from preferences. 267 if (reason != extensions::UnloadedExtensionInfo::REASON_PROFILE_SHUTDOWN) 268 ForgetFileSystems(extension->id()); 269 270 // Unmount all of the provided file systems associated with this extension. 271 ProvidedFileSystemMap::iterator it = file_system_map_.begin(); 272 while (it != file_system_map_.end()) { 273 const ProvidedFileSystemInfo& file_system_info = 274 it->second->GetFileSystemInfo(); 275 // Advance the iterator beforehand, otherwise it will become invalidated 276 // by the UnmountFileSystem() call. 277 ++it; 278 if (file_system_info.extension_id() == extension->id()) { 279 bool result = UnmountFileSystem(file_system_info.extension_id(), 280 file_system_info.file_system_id()); 281 DCHECK(result); 282 } 283 } 284} 285 286void Service::OnExtensionLoaded(content::BrowserContext* browser_context, 287 const extensions::Extension* extension) { 288 RestoreFileSystems(extension->id()); 289} 290 291ProvidedFileSystemInterface* Service::GetProvidedFileSystem( 292 const std::string& mount_point_name) { 293 DCHECK(thread_checker_.CalledOnValidThread()); 294 295 const MountPointNameToKeyMap::const_iterator mapping_it = 296 mount_point_name_to_key_map_.find(mount_point_name); 297 if (mapping_it == mount_point_name_to_key_map_.end()) 298 return NULL; 299 300 const ProvidedFileSystemMap::const_iterator file_system_it = 301 file_system_map_.find(mapping_it->second); 302 if (file_system_it == file_system_map_.end()) 303 return NULL; 304 305 return file_system_it->second; 306} 307 308void Service::OnRequestUnmountStatus( 309 const ProvidedFileSystemInfo& file_system_info, 310 base::File::Error error) { 311 // Notify observers about failure in unmounting, since mount() will not be 312 // called by the provided file system. In case of success mount() will be 313 // invoked, and observers notified, so there is no need to call them now. 314 if (error != base::File::FILE_OK) { 315 FOR_EACH_OBSERVER(Observer, 316 observers_, 317 OnProvidedFileSystemUnmount(file_system_info, error)); 318 } 319} 320 321void Service::RememberFileSystems() { 322 base::DictionaryValue extensions; 323 const std::vector<ProvidedFileSystemInfo> file_system_info_list = 324 GetProvidedFileSystemInfoList(); 325 326 for (std::vector<ProvidedFileSystemInfo>::const_iterator it = 327 file_system_info_list.begin(); 328 it != file_system_info_list.end(); 329 ++it) { 330 base::ListValue* file_systems = NULL; 331 if (!extensions.GetList(it->extension_id(), &file_systems)) { 332 file_systems = new base::ListValue(); 333 extensions.Set(it->extension_id(), file_systems); 334 } 335 336 base::DictionaryValue* file_system = new base::DictionaryValue(); 337 file_system->SetString(kPrefKeyFileSystemId, it->file_system_id()); 338 file_system->SetString(kPrefKeyFileSystemName, it->file_system_name()); 339 file_systems->Append(file_system); 340 } 341 342 PrefService* pref_service = profile_->GetPrefs(); 343 DCHECK(pref_service); 344 pref_service->Set(prefs::kFileSystemProviderMounted, extensions); 345 pref_service->CommitPendingWrite(); 346} 347 348void Service::ForgetFileSystems(const std::string& extension_id) { 349 PrefService* pref_service = profile_->GetPrefs(); 350 DCHECK(pref_service); 351 352 DictionaryPrefUpdate update(pref_service, prefs::kFileSystemProviderMounted); 353 base::DictionaryValue* extensions = update.Get(); 354 DCHECK(extensions); 355 356 extensions->Remove(extension_id, NULL); 357} 358 359void Service::RestoreFileSystems(const std::string& extension_id) { 360 PrefService* pref_service = profile_->GetPrefs(); 361 DCHECK(pref_service); 362 363 const base::DictionaryValue* extensions = 364 pref_service->GetDictionary(prefs::kFileSystemProviderMounted); 365 DCHECK(extensions); 366 367 const base::ListValue* file_systems = NULL; 368 369 if (!extensions->GetList(extension_id, &file_systems)) 370 return; 371 372 for (size_t i = 0; i < file_systems->GetSize(); ++i) { 373 const base::DictionaryValue* file_system = NULL; 374 file_systems->GetDictionary(i, &file_system); 375 DCHECK(file_system); 376 377 std::string file_system_id; 378 file_system->GetString(kPrefKeyFileSystemId, &file_system_id); 379 DCHECK(!file_system_id.empty()); 380 381 std::string file_system_name; 382 file_system->GetString(kPrefKeyFileSystemName, &file_system_name); 383 DCHECK(!file_system_name.empty()); 384 385 if (file_system_id.empty() || file_system_name.empty()) { 386 LOG(ERROR) 387 << "Malformed provided file system information in preferences."; 388 continue; 389 } 390 391 const bool result = 392 MountFileSystem(extension_id, file_system_id, file_system_name); 393 if (!result) { 394 LOG(ERROR) << "Failed to restore a provided file system from " 395 << "preferences: " << extension_id << ", " << file_system_id 396 << ", " << file_system_name << "."; 397 } 398 } 399} 400 401} // namespace file_system_provider 402} // namespace chromeos 403