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/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"
21 using content::BrowserThread
;
24 namespace file_system_provider
{
27 // Maximum number of file systems to be mounted in the same time, per profile.
28 const size_t kMaxFileSystems
= 16;
30 // Default factory for provided file systems. The |event_router| must not be
32 ProvidedFileSystemInterface
* CreateProvidedFileSystem(
33 extensions::EventRouter
* event_router
,
34 const ProvidedFileSystemInfo
& file_system_info
) {
36 return new ProvidedFileSystem(event_router
, file_system_info
);
41 Service::Service(Profile
* profile
,
42 extensions::ExtensionRegistry
* extension_registry
)
44 extension_registry_(extension_registry
),
45 file_system_factory_(base::Bind(CreateProvidedFileSystem
)),
47 weak_ptr_factory_(this) {
48 extension_registry_
->AddObserver(this);
52 extension_registry_
->RemoveObserver(this);
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();
60 UnmountFileSystem(extension_id
, file_system_id
);
63 DCHECK_EQ(0u, file_system_map_
.size());
64 STLDeleteValues(&file_system_map_
);
68 Service
* Service::Get(content::BrowserContext
* context
) {
69 return ServiceFactory::Get(context
);
72 void Service::AddObserver(Observer
* observer
) {
74 observers_
.AddObserver(observer
);
77 void Service::RemoveObserver(Observer
* observer
) {
79 observers_
.RemoveObserver(observer
);
82 void Service::SetFileSystemFactoryForTests(
83 const FileSystemFactoryCallback
& factory_callback
) {
84 DCHECK(!factory_callback
.is_null());
85 file_system_factory_
= factory_callback
;
88 int Service::MountFileSystem(const std::string
& extension_id
,
89 const std::string
& file_system_name
) {
90 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
92 // Restrict number of file systems to prevent system abusing.
93 if (file_system_map_
.size() + 1 > kMaxFileSystems
) {
97 OnProvidedFileSystemMount(ProvidedFileSystemInfo(),
98 base::File::FILE_ERROR_TOO_MANY_OPENED
));
102 // The provided file system id is unique per service, so per profile.
103 int file_system_id
= next_id_
;
105 fileapi::ExternalMountPoints
* const mount_points
=
106 fileapi::ExternalMountPoints::GetSystemInstance();
107 DCHECK(mount_points
);
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();
115 if (!mount_points
->RegisterFileSystem(mount_point_name
,
116 fileapi::kFileSystemTypeProvided
,
117 fileapi::FileSystemMountOption(),
122 OnProvidedFileSystemMount(ProvidedFileSystemInfo(),
123 base::File::FILE_ERROR_INVALID_OPERATION
));
127 // Store the file system descriptor. Use the mount point name as the file
128 // system provider file system id.
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
);
136 // The event router may be NULL for unit tests.
137 extensions::EventRouter
* router
= extensions::EventRouter::Get(profile_
);
139 ProvidedFileSystemInterface
* file_system
=
140 file_system_factory_
.Run(router
, file_system_info
);
142 file_system_map_
[file_system_id
] = file_system
;
143 mount_point_name_to_id_map_
[mount_point_name
] = file_system_id
;
148 OnProvidedFileSystemMount(file_system_info
, base::File::FILE_OK
));
151 return file_system_id
;
154 bool Service::UnmountFileSystem(const std::string
& extension_id
,
155 int file_system_id
) {
156 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
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() !=
163 const ProvidedFileSystemInfo empty_file_system_info
;
167 OnProvidedFileSystemUnmount(empty_file_system_info
,
168 base::File::FILE_ERROR_NOT_FOUND
));
172 fileapi::ExternalMountPoints
* const mount_points
=
173 fileapi::ExternalMountPoints::GetSystemInstance();
174 DCHECK(mount_points
);
176 const ProvidedFileSystemInfo
& file_system_info
=
177 file_system_it
->second
->GetFileSystemInfo();
179 const std::string mount_point_name
=
180 file_system_info
.mount_path().BaseName().value();
181 if (!mount_points
->RevokeFileSystem(mount_point_name
)) {
185 OnProvidedFileSystemUnmount(file_system_info
,
186 base::File::FILE_ERROR_INVALID_OPERATION
));
193 OnProvidedFileSystemUnmount(file_system_info
, base::File::FILE_OK
));
195 mount_point_name_to_id_map_
.erase(mount_point_name
);
197 delete file_system_it
->second
;
198 file_system_map_
.erase(file_system_it
);
203 bool Service::RequestUnmount(int file_system_id
) {
204 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
206 ProvidedFileSystemMap::iterator file_system_it
=
207 file_system_map_
.find(file_system_id
);
208 if (file_system_it
== file_system_map_
.end())
211 file_system_it
->second
->RequestUnmount(
212 base::Bind(&Service::OnRequestUnmountStatus
,
213 weak_ptr_factory_
.GetWeakPtr(),
214 file_system_it
->second
->GetFileSystemInfo()));
218 std::vector
<ProvidedFileSystemInfo
> Service::GetProvidedFileSystemInfoList() {
219 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
221 std::vector
<ProvidedFileSystemInfo
> result
;
222 for (ProvidedFileSystemMap::const_iterator it
= file_system_map_
.begin();
223 it
!= file_system_map_
.end();
225 result
.push_back(it
->second
->GetFileSystemInfo());
230 ProvidedFileSystemInterface
* Service::GetProvidedFileSystem(
231 const std::string
& extension_id
,
232 int file_system_id
) {
233 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
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() !=
243 return file_system_it
->second
;
246 void 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.
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());
266 ProvidedFileSystemInterface
* Service::GetProvidedFileSystem(
267 const std::string
& mount_point_name
) {
268 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
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())
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())
280 return file_system_it
->second
;
283 void 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
,
292 OnProvidedFileSystemUnmount(file_system_info
, error
));
296 } // namespace file_system_provider
297 } // namespace chromeos