• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
24 namespace chromeos {
25 namespace file_system_provider {
26 namespace {
27 
28 // Maximum number of file systems to be mounted in the same time, per profile.
29 const size_t kMaxFileSystems = 16;
30 
31 // Default factory for provided file systems. The |event_router| must not be
32 // NULL.
CreateProvidedFileSystem(extensions::EventRouter * event_router,const ProvidedFileSystemInfo & file_system_info)33 ProvidedFileSystemInterface* 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 
42 const char kPrefKeyFileSystemId[] = "file-system-id";
43 const char kPrefKeyFileSystemName[] = "file-system-name";
44 
RegisterProfilePrefs(user_prefs::PrefRegistrySyncable * registry)45 void RegisterProfilePrefs(user_prefs::PrefRegistrySyncable* registry) {
46   registry->RegisterDictionaryPref(
47       prefs::kFileSystemProviderMounted,
48       user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF);
49 }
50 
Service(Profile * profile,extensions::ExtensionRegistry * extension_registry)51 Service::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 
~Service()60 Service::~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
Get(content::BrowserContext * context)79 Service* Service::Get(content::BrowserContext* context) {
80   return ServiceFactory::Get(context);
81 }
82 
AddObserver(Observer * observer)83 void Service::AddObserver(Observer* observer) {
84   DCHECK(observer);
85   observers_.AddObserver(observer);
86 }
87 
RemoveObserver(Observer * observer)88 void Service::RemoveObserver(Observer* observer) {
89   DCHECK(observer);
90   observers_.RemoveObserver(observer);
91 }
92 
SetFileSystemFactoryForTesting(const FileSystemFactoryCallback & factory_callback)93 void Service::SetFileSystemFactoryForTesting(
94     const FileSystemFactoryCallback& factory_callback) {
95   DCHECK(!factory_callback.is_null());
96   file_system_factory_ = factory_callback;
97 }
98 
MountFileSystem(const std::string & extension_id,const std::string & file_system_id,const std::string & file_system_name)99 bool 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 
UnmountFileSystem(const std::string & extension_id,const std::string & file_system_id)173 bool 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 
RequestUnmount(const std::string & extension_id,const std::string & file_system_id)220 bool 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 
GetProvidedFileSystemInfoList()236 std::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 
GetProvidedFileSystem(const std::string & extension_id,const std::string & file_system_id)248 ProvidedFileSystemInterface* 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 
OnExtensionUnloaded(content::BrowserContext * browser_context,const extensions::Extension * extension,extensions::UnloadedExtensionInfo::Reason reason)261 void 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 
OnExtensionLoaded(content::BrowserContext * browser_context,const extensions::Extension * extension)286 void Service::OnExtensionLoaded(content::BrowserContext* browser_context,
287                                 const extensions::Extension* extension) {
288   RestoreFileSystems(extension->id());
289 }
290 
GetProvidedFileSystem(const std::string & mount_point_name)291 ProvidedFileSystemInterface* 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 
OnRequestUnmountStatus(const ProvidedFileSystemInfo & file_system_info,base::File::Error error)308 void 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 
RememberFileSystems()321 void 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 
ForgetFileSystems(const std::string & extension_id)348 void 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 
RestoreFileSystems(const std::string & extension_id)359 void 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