Revert of Add button to add new FSP services to Files app. (patchset #8 id:140001...
[chromium-blink-merge.git] / chrome / browser / chromeos / file_system_provider / service.cc
blobfa4699151a3d5858df0316e7684cf20f5a5c8e95
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.
5 #include "chrome/browser/chromeos/file_system_provider/service.h"
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/registry.h"
16 #include "chrome/browser/chromeos/file_system_provider/registry_interface.h"
17 #include "chrome/browser/chromeos/file_system_provider/service_factory.h"
18 #include "chrome/browser/chromeos/file_system_provider/throttled_file_system.h"
19 #include "extensions/browser/extension_registry.h"
20 #include "extensions/browser/extension_system.h"
21 #include "storage/browser/fileapi/external_mount_points.h"
22 #include "storage/common/fileapi/file_system_mount_option.h"
24 namespace chromeos {
25 namespace file_system_provider {
26 namespace {
28 // Maximum number of file systems to be mounted in the same time, per profile.
29 const size_t kMaxFileSystems = 16;
31 // Default factory for provided file systems. |profile| must not be NULL.
32 ProvidedFileSystemInterface* CreateProvidedFileSystem(
33 Profile* profile,
34 const ProvidedFileSystemInfo& file_system_info) {
35 DCHECK(profile);
36 return new ThrottledFileSystem(
37 make_scoped_ptr(new ProvidedFileSystem(profile, file_system_info)));
40 } // namespace
42 Service::Service(Profile* profile,
43 extensions::ExtensionRegistry* extension_registry)
44 : profile_(profile),
45 extension_registry_(extension_registry),
46 file_system_factory_(base::Bind(&CreateProvidedFileSystem)),
47 registry_(new Registry(profile)),
48 weak_ptr_factory_(this) {
49 extension_registry_->AddObserver(this);
52 Service::~Service() {
53 extension_registry_->RemoveObserver(this);
55 // Provided file systems should be already unmounted because of receiving
56 // OnExtensionUnload calls for each installed extension. However, for tests
57 // we may still have mounted extensions.
58 // TODO(mtomasz): Create a TestingService class and remove this code.
59 ProvidedFileSystemMap::iterator it = file_system_map_.begin();
60 while (it != file_system_map_.end()) {
61 const std::string file_system_id =
62 it->second->GetFileSystemInfo().file_system_id();
63 const std::string extension_id =
64 it->second->GetFileSystemInfo().extension_id();
65 ++it;
66 const base::File::Error unmount_result = UnmountFileSystem(
67 extension_id, file_system_id, UNMOUNT_REASON_SHUTDOWN);
68 DCHECK_EQ(base::File::FILE_OK, unmount_result);
71 DCHECK_EQ(0u, file_system_map_.size());
72 STLDeleteValues(&file_system_map_);
75 // static
76 Service* Service::Get(content::BrowserContext* context) {
77 return ServiceFactory::Get(context);
80 void Service::AddObserver(Observer* observer) {
81 DCHECK(observer);
82 observers_.AddObserver(observer);
85 void Service::RemoveObserver(Observer* observer) {
86 DCHECK(observer);
87 observers_.RemoveObserver(observer);
90 void Service::SetFileSystemFactoryForTesting(
91 const FileSystemFactoryCallback& factory_callback) {
92 DCHECK(!factory_callback.is_null());
93 file_system_factory_ = factory_callback;
96 void Service::SetRegistryForTesting(scoped_ptr<RegistryInterface> registry) {
97 DCHECK(registry);
98 registry_.reset(registry.release());
101 base::File::Error Service::MountFileSystem(const std::string& extension_id,
102 const MountOptions& options) {
103 return MountFileSystemInternal(extension_id, options, MOUNT_CONTEXT_USER);
106 base::File::Error Service::MountFileSystemInternal(
107 const std::string& extension_id,
108 const MountOptions& options,
109 MountContext context) {
110 DCHECK(thread_checker_.CalledOnValidThread());
112 // If already exists a file system provided by the same extension with this
113 // id, then abort.
114 if (GetProvidedFileSystem(extension_id, options.file_system_id)) {
115 FOR_EACH_OBSERVER(
116 Observer, observers_,
117 OnProvidedFileSystemMount(ProvidedFileSystemInfo(), context,
118 base::File::FILE_ERROR_EXISTS));
119 return base::File::FILE_ERROR_EXISTS;
122 // Restrict number of file systems to prevent system abusing.
123 if (file_system_map_.size() + 1 > kMaxFileSystems) {
124 FOR_EACH_OBSERVER(
125 Observer, observers_,
126 OnProvidedFileSystemMount(ProvidedFileSystemInfo(), context,
127 base::File::FILE_ERROR_TOO_MANY_OPENED));
128 return base::File::FILE_ERROR_TOO_MANY_OPENED;
131 storage::ExternalMountPoints* const mount_points =
132 storage::ExternalMountPoints::GetSystemInstance();
133 DCHECK(mount_points);
135 // The mount point path and name are unique per system, since they are system
136 // wide. This is necessary for copying between profiles.
137 const base::FilePath& mount_path =
138 util::GetMountPath(profile_, extension_id, options.file_system_id);
139 const std::string mount_point_name = mount_path.BaseName().AsUTF8Unsafe();
141 if (!mount_points->RegisterFileSystem(
142 mount_point_name, storage::kFileSystemTypeProvided,
143 storage::FileSystemMountOption(
144 storage::FlushPolicy::FLUSH_ON_COMPLETION),
145 mount_path)) {
146 FOR_EACH_OBSERVER(
147 Observer, observers_,
148 OnProvidedFileSystemMount(ProvidedFileSystemInfo(), context,
149 base::File::FILE_ERROR_INVALID_OPERATION));
150 return base::File::FILE_ERROR_INVALID_OPERATION;
153 // Store the file system descriptor. Use the mount point name as the file
154 // system provider file system id.
155 // Examples:
156 // file_system_id = hello_world
157 // mount_point_name = b33f1337-hello_world-5aa5
158 // writable = false
159 // supports_notify_tag = false
160 // mount_path = /provided/b33f1337-hello_world-5aa5
161 ProvidedFileSystemInfo file_system_info(extension_id, options, mount_path);
163 ProvidedFileSystemInterface* file_system =
164 file_system_factory_.Run(profile_, file_system_info);
165 DCHECK(file_system);
166 file_system_map_[FileSystemKey(extension_id, options.file_system_id)] =
167 file_system;
168 mount_point_name_to_key_map_[mount_point_name] =
169 FileSystemKey(extension_id, options.file_system_id);
170 registry_->RememberFileSystem(file_system_info, *file_system->GetWatchers());
172 FOR_EACH_OBSERVER(Observer, observers_,
173 OnProvidedFileSystemMount(file_system_info, context,
174 base::File::FILE_OK));
176 return base::File::FILE_OK;
179 base::File::Error Service::UnmountFileSystem(const std::string& extension_id,
180 const std::string& file_system_id,
181 UnmountReason reason) {
182 DCHECK(thread_checker_.CalledOnValidThread());
184 const ProvidedFileSystemMap::iterator file_system_it =
185 file_system_map_.find(FileSystemKey(extension_id, file_system_id));
186 if (file_system_it == file_system_map_.end()) {
187 const ProvidedFileSystemInfo empty_file_system_info;
188 FOR_EACH_OBSERVER(
189 Observer,
190 observers_,
191 OnProvidedFileSystemUnmount(empty_file_system_info,
192 base::File::FILE_ERROR_NOT_FOUND));
193 return base::File::FILE_ERROR_NOT_FOUND;
196 storage::ExternalMountPoints* const mount_points =
197 storage::ExternalMountPoints::GetSystemInstance();
198 DCHECK(mount_points);
200 const ProvidedFileSystemInfo& file_system_info =
201 file_system_it->second->GetFileSystemInfo();
203 const std::string mount_point_name =
204 file_system_info.mount_path().BaseName().value();
205 if (!mount_points->RevokeFileSystem(mount_point_name)) {
206 FOR_EACH_OBSERVER(
207 Observer,
208 observers_,
209 OnProvidedFileSystemUnmount(file_system_info,
210 base::File::FILE_ERROR_INVALID_OPERATION));
211 return base::File::FILE_ERROR_INVALID_OPERATION;
214 FOR_EACH_OBSERVER(
215 Observer,
216 observers_,
217 OnProvidedFileSystemUnmount(file_system_info, base::File::FILE_OK));
219 mount_point_name_to_key_map_.erase(mount_point_name);
221 if (reason == UNMOUNT_REASON_USER) {
222 registry_->ForgetFileSystem(file_system_info.extension_id(),
223 file_system_info.file_system_id());
226 delete file_system_it->second;
227 file_system_map_.erase(file_system_it);
229 return base::File::FILE_OK;
232 bool Service::RequestUnmount(const std::string& extension_id,
233 const std::string& file_system_id) {
234 DCHECK(thread_checker_.CalledOnValidThread());
236 ProvidedFileSystemMap::iterator file_system_it =
237 file_system_map_.find(FileSystemKey(extension_id, file_system_id));
238 if (file_system_it == file_system_map_.end())
239 return false;
241 file_system_it->second->RequestUnmount(
242 base::Bind(&Service::OnRequestUnmountStatus,
243 weak_ptr_factory_.GetWeakPtr(),
244 file_system_it->second->GetFileSystemInfo()));
245 return true;
248 std::vector<ProvidedFileSystemInfo> Service::GetProvidedFileSystemInfoList() {
249 DCHECK(thread_checker_.CalledOnValidThread());
251 std::vector<ProvidedFileSystemInfo> result;
252 for (ProvidedFileSystemMap::const_iterator it = file_system_map_.begin();
253 it != file_system_map_.end();
254 ++it) {
255 result.push_back(it->second->GetFileSystemInfo());
257 return result;
260 ProvidedFileSystemInterface* Service::GetProvidedFileSystem(
261 const std::string& extension_id,
262 const std::string& file_system_id) {
263 DCHECK(thread_checker_.CalledOnValidThread());
265 const ProvidedFileSystemMap::const_iterator file_system_it =
266 file_system_map_.find(FileSystemKey(extension_id, file_system_id));
267 if (file_system_it == file_system_map_.end())
268 return NULL;
270 return file_system_it->second;
273 void Service::OnExtensionUnloaded(
274 content::BrowserContext* browser_context,
275 const extensions::Extension* extension,
276 extensions::UnloadedExtensionInfo::Reason reason) {
277 // Unmount all of the provided file systems associated with this extension.
278 ProvidedFileSystemMap::iterator it = file_system_map_.begin();
279 while (it != file_system_map_.end()) {
280 const ProvidedFileSystemInfo& file_system_info =
281 it->second->GetFileSystemInfo();
282 // Advance the iterator beforehand, otherwise it will become invalidated
283 // by the UnmountFileSystem() call.
284 ++it;
285 if (file_system_info.extension_id() == extension->id()) {
286 const base::File::Error unmount_result = UnmountFileSystem(
287 file_system_info.extension_id(), file_system_info.file_system_id(),
288 reason == extensions::UnloadedExtensionInfo::REASON_PROFILE_SHUTDOWN
289 ? UNMOUNT_REASON_SHUTDOWN
290 : UNMOUNT_REASON_USER);
291 DCHECK_EQ(base::File::FILE_OK, unmount_result);
296 void Service::OnExtensionLoaded(content::BrowserContext* browser_context,
297 const extensions::Extension* extension) {
298 scoped_ptr<RegistryInterface::RestoredFileSystems> restored_file_systems =
299 registry_->RestoreFileSystems(extension->id());
301 for (const auto& restored_file_system : *restored_file_systems) {
302 const base::File::Error result = MountFileSystemInternal(
303 restored_file_system.extension_id, restored_file_system.options,
304 MOUNT_CONTEXT_RESTORE);
305 if (result != base::File::FILE_OK) {
306 LOG(ERROR) << "Failed to restore a provided file system from "
307 << "registry: " << restored_file_system.extension_id << ", "
308 << restored_file_system.options.file_system_id << ", "
309 << restored_file_system.options.display_name << ".";
310 // Since remounting of the file system failed, then remove it from
311 // preferences to avoid remounting it over and over again with a failure.
312 registry_->ForgetFileSystem(restored_file_system.extension_id,
313 restored_file_system.options.file_system_id);
314 continue;
317 ProvidedFileSystemInterface* const file_system =
318 GetProvidedFileSystem(restored_file_system.extension_id,
319 restored_file_system.options.file_system_id);
320 DCHECK(file_system);
321 file_system->GetWatchers()->insert(restored_file_system.watchers.begin(),
322 restored_file_system.watchers.end());
326 ProvidedFileSystemInterface* Service::GetProvidedFileSystem(
327 const std::string& mount_point_name) {
328 DCHECK(thread_checker_.CalledOnValidThread());
330 const MountPointNameToKeyMap::const_iterator mapping_it =
331 mount_point_name_to_key_map_.find(mount_point_name);
332 if (mapping_it == mount_point_name_to_key_map_.end())
333 return NULL;
335 const ProvidedFileSystemMap::const_iterator file_system_it =
336 file_system_map_.find(mapping_it->second);
337 if (file_system_it == file_system_map_.end())
338 return NULL;
340 return file_system_it->second;
343 void Service::OnRequestUnmountStatus(
344 const ProvidedFileSystemInfo& file_system_info,
345 base::File::Error error) {
346 // Notify observers about failure in unmounting, since mount() will not be
347 // called by the provided file system. In case of success mount() will be
348 // invoked, and observers notified, so there is no need to call them now.
349 if (error != base::File::FILE_OK) {
350 FOR_EACH_OBSERVER(Observer,
351 observers_,
352 OnProvidedFileSystemUnmount(file_system_info, error));
356 void Service::OnWatcherChanged(const ProvidedFileSystemInfo& file_system_info,
357 const Watcher& watcher,
358 storage::WatcherManager::ChangeType change_type,
359 const Changes& changes,
360 const base::Closure& callback) {
361 callback.Run();
364 void Service::OnWatcherTagUpdated(
365 const ProvidedFileSystemInfo& file_system_info,
366 const Watcher& watcher) {
367 PrefService* const pref_service = profile_->GetPrefs();
368 DCHECK(pref_service);
370 registry_->UpdateWatcherTag(file_system_info, watcher);
373 void Service::OnWatcherListChanged(
374 const ProvidedFileSystemInfo& file_system_info,
375 const Watchers& watchers) {
376 registry_->RememberFileSystem(file_system_info, watchers);
379 } // namespace file_system_provider
380 } // namespace chromeos