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