service.cc revision 010d83a9304c5a91596085d917d248abff47903a
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/stl_util.h" 9#include "chrome/browser/chromeos/file_system_provider/mount_path_util.h" 10#include "chrome/browser/chromeos/file_system_provider/observer.h" 11#include "chrome/browser/chromeos/file_system_provider/provided_file_system.h" 12#include "chrome/browser/chromeos/file_system_provider/provided_file_system_info.h" 13#include "chrome/browser/chromeos/file_system_provider/provided_file_system_interface.h" 14#include "chrome/browser/chromeos/file_system_provider/service_factory.h" 15#include "content/public/browser/browser_thread.h" 16#include "extensions/browser/event_router.h" 17#include "extensions/browser/extension_registry.h" 18#include "extensions/browser/extension_system.h" 19#include "webkit/browser/fileapi/external_mount_points.h" 20 21using content::BrowserThread; 22 23namespace chromeos { 24namespace file_system_provider { 25namespace { 26 27// Maximum number of file systems to be mounted in the same time, per profile. 28const size_t kMaxFileSystems = 16; 29 30// Default factory for provided file systems. The |event_router| must not be 31// NULL. 32ProvidedFileSystemInterface* CreateProvidedFileSystem( 33 extensions::EventRouter* event_router, 34 const ProvidedFileSystemInfo& file_system_info) { 35 DCHECK(event_router); 36 return new ProvidedFileSystem(event_router, file_system_info); 37} 38 39} // namespace 40 41Service::Service(Profile* profile, 42 extensions::ExtensionRegistry* extension_registry) 43 : profile_(profile), 44 extension_registry_(extension_registry), 45 file_system_factory_(base::Bind(CreateProvidedFileSystem)), 46 next_id_(1), 47 weak_ptr_factory_(this) { 48 extension_registry_->AddObserver(this); 49} 50 51Service::~Service() { 52 extension_registry_->RemoveObserver(this); 53 54 ProvidedFileSystemMap::iterator it = file_system_map_.begin(); 55 while (it != file_system_map_.end()) { 56 const int file_system_id = it->first; 57 const std::string extension_id = 58 it->second->GetFileSystemInfo().extension_id(); 59 ++it; 60 UnmountFileSystem(extension_id, file_system_id); 61 } 62 63 DCHECK_EQ(0u, file_system_map_.size()); 64 STLDeleteValues(&file_system_map_); 65} 66 67// static 68Service* Service::Get(content::BrowserContext* context) { 69 return ServiceFactory::Get(context); 70} 71 72void Service::AddObserver(Observer* observer) { 73 DCHECK(observer); 74 observers_.AddObserver(observer); 75} 76 77void Service::RemoveObserver(Observer* observer) { 78 DCHECK(observer); 79 observers_.RemoveObserver(observer); 80} 81 82void Service::SetFileSystemFactoryForTests( 83 const FileSystemFactoryCallback& factory_callback) { 84 DCHECK(!factory_callback.is_null()); 85 file_system_factory_ = factory_callback; 86} 87 88int Service::MountFileSystem(const std::string& extension_id, 89 const std::string& file_system_name) { 90 DCHECK_CURRENTLY_ON(BrowserThread::UI); 91 92 // Restrict number of file systems to prevent system abusing. 93 if (file_system_map_.size() + 1 > kMaxFileSystems) { 94 FOR_EACH_OBSERVER( 95 Observer, 96 observers_, 97 OnProvidedFileSystemMount(ProvidedFileSystemInfo(), 98 base::File::FILE_ERROR_TOO_MANY_OPENED)); 99 return 0; 100 } 101 102 // The provided file system id is unique per service, so per profile. 103 int file_system_id = next_id_; 104 105 fileapi::ExternalMountPoints* const mount_points = 106 fileapi::ExternalMountPoints::GetSystemInstance(); 107 DCHECK(mount_points); 108 109 // The mount point path and name are unique per system, since they are system 110 // wide. This is necessary for copying between profiles. 111 const base::FilePath& mount_path = 112 util::GetMountPath(profile_, extension_id, file_system_id); 113 const std::string mount_point_name = mount_path.BaseName().AsUTF8Unsafe(); 114 115 if (!mount_points->RegisterFileSystem(mount_point_name, 116 fileapi::kFileSystemTypeProvided, 117 fileapi::FileSystemMountOption(), 118 mount_path)) { 119 FOR_EACH_OBSERVER( 120 Observer, 121 observers_, 122 OnProvidedFileSystemMount(ProvidedFileSystemInfo(), 123 base::File::FILE_ERROR_INVALID_OPERATION)); 124 return 0; 125 } 126 127 // Store the file system descriptor. Use the mount point name as the file 128 // system provider file system id. 129 // Examples: 130 // file_system_id = 41 131 // mount_point_name = b33f1337-41-5aa5 132 // mount_path = /provided/b33f1337-41-5aa5 133 ProvidedFileSystemInfo file_system_info( 134 extension_id, file_system_id, file_system_name, mount_path); 135 136 // The event router may be NULL for unit tests. 137 extensions::EventRouter* router = extensions::EventRouter::Get(profile_); 138 139 ProvidedFileSystemInterface* file_system = 140 file_system_factory_.Run(router, file_system_info); 141 DCHECK(file_system); 142 file_system_map_[file_system_id] = file_system; 143 mount_point_name_to_id_map_[mount_point_name] = file_system_id; 144 145 FOR_EACH_OBSERVER( 146 Observer, 147 observers_, 148 OnProvidedFileSystemMount(file_system_info, base::File::FILE_OK)); 149 150 next_id_++; 151 return file_system_id; 152} 153 154bool Service::UnmountFileSystem(const std::string& extension_id, 155 int file_system_id) { 156 DCHECK_CURRENTLY_ON(BrowserThread::UI); 157 158 const ProvidedFileSystemMap::iterator file_system_it = 159 file_system_map_.find(file_system_id); 160 if (file_system_it == file_system_map_.end() || 161 file_system_it->second->GetFileSystemInfo().extension_id() != 162 extension_id) { 163 const ProvidedFileSystemInfo empty_file_system_info; 164 FOR_EACH_OBSERVER( 165 Observer, 166 observers_, 167 OnProvidedFileSystemUnmount(empty_file_system_info, 168 base::File::FILE_ERROR_NOT_FOUND)); 169 return false; 170 } 171 172 fileapi::ExternalMountPoints* const mount_points = 173 fileapi::ExternalMountPoints::GetSystemInstance(); 174 DCHECK(mount_points); 175 176 const ProvidedFileSystemInfo& file_system_info = 177 file_system_it->second->GetFileSystemInfo(); 178 179 const std::string mount_point_name = 180 file_system_info.mount_path().BaseName().value(); 181 if (!mount_points->RevokeFileSystem(mount_point_name)) { 182 FOR_EACH_OBSERVER( 183 Observer, 184 observers_, 185 OnProvidedFileSystemUnmount(file_system_info, 186 base::File::FILE_ERROR_INVALID_OPERATION)); 187 return false; 188 } 189 190 FOR_EACH_OBSERVER( 191 Observer, 192 observers_, 193 OnProvidedFileSystemUnmount(file_system_info, base::File::FILE_OK)); 194 195 mount_point_name_to_id_map_.erase(mount_point_name); 196 197 delete file_system_it->second; 198 file_system_map_.erase(file_system_it); 199 200 return true; 201} 202 203bool Service::RequestUnmount(int file_system_id) { 204 DCHECK_CURRENTLY_ON(BrowserThread::UI); 205 206 ProvidedFileSystemMap::iterator file_system_it = 207 file_system_map_.find(file_system_id); 208 if (file_system_it == file_system_map_.end()) 209 return false; 210 211 file_system_it->second->RequestUnmount( 212 base::Bind(&Service::OnRequestUnmountStatus, 213 weak_ptr_factory_.GetWeakPtr(), 214 file_system_it->second->GetFileSystemInfo())); 215 return true; 216} 217 218std::vector<ProvidedFileSystemInfo> Service::GetProvidedFileSystemInfoList() { 219 DCHECK_CURRENTLY_ON(BrowserThread::UI); 220 221 std::vector<ProvidedFileSystemInfo> result; 222 for (ProvidedFileSystemMap::const_iterator it = file_system_map_.begin(); 223 it != file_system_map_.end(); 224 ++it) { 225 result.push_back(it->second->GetFileSystemInfo()); 226 } 227 return result; 228} 229 230ProvidedFileSystemInterface* Service::GetProvidedFileSystem( 231 const std::string& extension_id, 232 int file_system_id) { 233 DCHECK_CURRENTLY_ON(BrowserThread::UI); 234 235 const ProvidedFileSystemMap::const_iterator file_system_it = 236 file_system_map_.find(file_system_id); 237 if (file_system_it == file_system_map_.end() || 238 file_system_it->second->GetFileSystemInfo().extension_id() != 239 extension_id) { 240 return NULL; 241 } 242 243 return file_system_it->second; 244} 245 246void Service::OnExtensionUnloaded( 247 content::BrowserContext* browser_context, 248 const extensions::Extension* extension, 249 extensions::UnloadedExtensionInfo::Reason reason) { 250 // Unmount all of the provided file systems associated with this extension. 251 ProvidedFileSystemMap::iterator it = file_system_map_.begin(); 252 while (it != file_system_map_.end()) { 253 const ProvidedFileSystemInfo& file_system_info = 254 it->second->GetFileSystemInfo(); 255 // Advance the iterator beforehand, otherwise it will become invalidated 256 // by the UnmountFileSystem() call. 257 ++it; 258 if (file_system_info.extension_id() == extension->id()) { 259 bool result = UnmountFileSystem(file_system_info.extension_id(), 260 file_system_info.file_system_id()); 261 DCHECK(result); 262 } 263 } 264} 265 266ProvidedFileSystemInterface* Service::GetProvidedFileSystem( 267 const std::string& mount_point_name) { 268 DCHECK_CURRENTLY_ON(BrowserThread::UI); 269 270 const MountPointNameToIdMap::const_iterator mapping_it = 271 mount_point_name_to_id_map_.find(mount_point_name); 272 if (mapping_it == mount_point_name_to_id_map_.end()) 273 return NULL; 274 275 const ProvidedFileSystemMap::const_iterator file_system_it = 276 file_system_map_.find(mapping_it->second); 277 if (file_system_it == file_system_map_.end()) 278 return NULL; 279 280 return file_system_it->second; 281} 282 283void Service::OnRequestUnmountStatus( 284 const ProvidedFileSystemInfo& file_system_info, 285 base::File::Error error) { 286 // Notify observers about failure in unmounting, since mount() will not be 287 // called by the provided file system. In case of success mount() will be 288 // invoked, and observers notified, so there is no need to call them now. 289 if (error != base::File::FILE_OK) { 290 FOR_EACH_OBSERVER(Observer, 291 observers_, 292 OnProvidedFileSystemUnmount(file_system_info, error)); 293 } 294} 295 296} // namespace file_system_provider 297} // namespace chromeos 298