1e6e8a0bd7cffcc9ae2e0e75546fb12a19213d4aeBen Cheng// Copyright 2014 The Chromium Authors. All rights reserved.
2e6e8a0bd7cffcc9ae2e0e75546fb12a19213d4aeBen Cheng// Use of this source code is governed by a BSD-style license that can be
3e6e8a0bd7cffcc9ae2e0e75546fb12a19213d4aeBen Cheng// found in the LICENSE file.
4e6e8a0bd7cffcc9ae2e0e75546fb12a19213d4aeBen Cheng
5e6e8a0bd7cffcc9ae2e0e75546fb12a19213d4aeBen Cheng#include "chrome/browser/chromeos/file_system_provider/service.h"
6e6e8a0bd7cffcc9ae2e0e75546fb12a19213d4aeBen Cheng
7e6e8a0bd7cffcc9ae2e0e75546fb12a19213d4aeBen Cheng#include "base/files/file_path.h"
8e6e8a0bd7cffcc9ae2e0e75546fb12a19213d4aeBen Cheng#include "base/prefs/pref_service.h"
9e6e8a0bd7cffcc9ae2e0e75546fb12a19213d4aeBen Cheng#include "base/prefs/scoped_user_pref_update.h"
10e6e8a0bd7cffcc9ae2e0e75546fb12a19213d4aeBen Cheng#include "base/stl_util.h"
11e6e8a0bd7cffcc9ae2e0e75546fb12a19213d4aeBen Cheng#include "chrome/browser/chromeos/file_system_provider/mount_path_util.h"
12e6e8a0bd7cffcc9ae2e0e75546fb12a19213d4aeBen Cheng#include "chrome/browser/chromeos/file_system_provider/observer.h"
13e6e8a0bd7cffcc9ae2e0e75546fb12a19213d4aeBen Cheng#include "chrome/browser/chromeos/file_system_provider/provided_file_system.h"
14e6e8a0bd7cffcc9ae2e0e75546fb12a19213d4aeBen Cheng#include "chrome/browser/chromeos/file_system_provider/provided_file_system_info.h"
15e6e8a0bd7cffcc9ae2e0e75546fb12a19213d4aeBen Cheng#include "chrome/browser/chromeos/file_system_provider/provided_file_system_interface.h"
16e6e8a0bd7cffcc9ae2e0e75546fb12a19213d4aeBen Cheng#include "chrome/browser/chromeos/file_system_provider/service_factory.h"
17e6e8a0bd7cffcc9ae2e0e75546fb12a19213d4aeBen Cheng#include "chrome/common/pref_names.h"
18e6e8a0bd7cffcc9ae2e0e75546fb12a19213d4aeBen Cheng#include "components/pref_registry/pref_registry_syncable.h"
19e6e8a0bd7cffcc9ae2e0e75546fb12a19213d4aeBen Cheng#include "extensions/browser/extension_registry.h"
20e6e8a0bd7cffcc9ae2e0e75546fb12a19213d4aeBen Cheng#include "extensions/browser/extension_system.h"
21e6e8a0bd7cffcc9ae2e0e75546fb12a19213d4aeBen Cheng#include "storage/browser/fileapi/external_mount_points.h"
22e6e8a0bd7cffcc9ae2e0e75546fb12a19213d4aeBen Cheng
23e6e8a0bd7cffcc9ae2e0e75546fb12a19213d4aeBen Chengnamespace chromeos {
24e6e8a0bd7cffcc9ae2e0e75546fb12a19213d4aeBen Chengnamespace file_system_provider {
25e6e8a0bd7cffcc9ae2e0e75546fb12a19213d4aeBen Chengnamespace {
26e6e8a0bd7cffcc9ae2e0e75546fb12a19213d4aeBen Cheng
27e6e8a0bd7cffcc9ae2e0e75546fb12a19213d4aeBen Cheng// Maximum number of file systems to be mounted in the same time, per profile.
28e6e8a0bd7cffcc9ae2e0e75546fb12a19213d4aeBen Chengconst size_t kMaxFileSystems = 16;
29e6e8a0bd7cffcc9ae2e0e75546fb12a19213d4aeBen Cheng
30e6e8a0bd7cffcc9ae2e0e75546fb12a19213d4aeBen Cheng// Default factory for provided file systems. |profile| must not be NULL.
31e6e8a0bd7cffcc9ae2e0e75546fb12a19213d4aeBen ChengProvidedFileSystemInterface* CreateProvidedFileSystem(
32e6e8a0bd7cffcc9ae2e0e75546fb12a19213d4aeBen Cheng    Profile* profile,
33e6e8a0bd7cffcc9ae2e0e75546fb12a19213d4aeBen Cheng    const ProvidedFileSystemInfo& file_system_info) {
34e6e8a0bd7cffcc9ae2e0e75546fb12a19213d4aeBen Cheng  DCHECK(profile);
35e6e8a0bd7cffcc9ae2e0e75546fb12a19213d4aeBen Cheng  return new ProvidedFileSystem(profile, file_system_info);
36e6e8a0bd7cffcc9ae2e0e75546fb12a19213d4aeBen Cheng}
37e6e8a0bd7cffcc9ae2e0e75546fb12a19213d4aeBen Cheng
38e6e8a0bd7cffcc9ae2e0e75546fb12a19213d4aeBen Cheng}  // namespace
39e6e8a0bd7cffcc9ae2e0e75546fb12a19213d4aeBen Cheng
40e6e8a0bd7cffcc9ae2e0e75546fb12a19213d4aeBen Chengconst char kPrefKeyFileSystemId[] = "file-system-id";
41e6e8a0bd7cffcc9ae2e0e75546fb12a19213d4aeBen Chengconst char kPrefKeyDisplayName[] = "display-name";
42e6e8a0bd7cffcc9ae2e0e75546fb12a19213d4aeBen Chengconst char kPrefKeyWritable[] = "writable";
43e6e8a0bd7cffcc9ae2e0e75546fb12a19213d4aeBen Cheng
44e6e8a0bd7cffcc9ae2e0e75546fb12a19213d4aeBen Chengvoid RegisterProfilePrefs(user_prefs::PrefRegistrySyncable* registry) {
45e6e8a0bd7cffcc9ae2e0e75546fb12a19213d4aeBen Cheng  registry->RegisterDictionaryPref(
46e6e8a0bd7cffcc9ae2e0e75546fb12a19213d4aeBen Cheng      prefs::kFileSystemProviderMounted,
47e6e8a0bd7cffcc9ae2e0e75546fb12a19213d4aeBen Cheng      user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF);
48e6e8a0bd7cffcc9ae2e0e75546fb12a19213d4aeBen Cheng}
49e6e8a0bd7cffcc9ae2e0e75546fb12a19213d4aeBen Cheng
50e6e8a0bd7cffcc9ae2e0e75546fb12a19213d4aeBen ChengService::Service(Profile* profile,
51e6e8a0bd7cffcc9ae2e0e75546fb12a19213d4aeBen Cheng                 extensions::ExtensionRegistry* extension_registry)
52e6e8a0bd7cffcc9ae2e0e75546fb12a19213d4aeBen Cheng    : profile_(profile),
53e6e8a0bd7cffcc9ae2e0e75546fb12a19213d4aeBen Cheng      extension_registry_(extension_registry),
54e6e8a0bd7cffcc9ae2e0e75546fb12a19213d4aeBen Cheng      file_system_factory_(base::Bind(CreateProvidedFileSystem)),
55e6e8a0bd7cffcc9ae2e0e75546fb12a19213d4aeBen Cheng      weak_ptr_factory_(this) {
56e6e8a0bd7cffcc9ae2e0e75546fb12a19213d4aeBen Cheng  extension_registry_->AddObserver(this);
57e6e8a0bd7cffcc9ae2e0e75546fb12a19213d4aeBen Cheng}
58e6e8a0bd7cffcc9ae2e0e75546fb12a19213d4aeBen Cheng
59e6e8a0bd7cffcc9ae2e0e75546fb12a19213d4aeBen ChengService::~Service() {
60e6e8a0bd7cffcc9ae2e0e75546fb12a19213d4aeBen Cheng  extension_registry_->RemoveObserver(this);
61e6e8a0bd7cffcc9ae2e0e75546fb12a19213d4aeBen Cheng
62e6e8a0bd7cffcc9ae2e0e75546fb12a19213d4aeBen Cheng  // Provided file systems should be already unmounted because of receiving
63e6e8a0bd7cffcc9ae2e0e75546fb12a19213d4aeBen Cheng  // OnExtensionUnload calls for each installed extension. However, for tests
64e6e8a0bd7cffcc9ae2e0e75546fb12a19213d4aeBen Cheng  // we may still have mounted extensions.
65e6e8a0bd7cffcc9ae2e0e75546fb12a19213d4aeBen Cheng  // TODO(mtomasz): Create a TestingService class and remove this code.
66e6e8a0bd7cffcc9ae2e0e75546fb12a19213d4aeBen Cheng  ProvidedFileSystemMap::iterator it = file_system_map_.begin();
67e6e8a0bd7cffcc9ae2e0e75546fb12a19213d4aeBen Cheng  while (it != file_system_map_.end()) {
68e6e8a0bd7cffcc9ae2e0e75546fb12a19213d4aeBen Cheng    const std::string file_system_id =
69e6e8a0bd7cffcc9ae2e0e75546fb12a19213d4aeBen Cheng        it->second->GetFileSystemInfo().file_system_id();
70e6e8a0bd7cffcc9ae2e0e75546fb12a19213d4aeBen Cheng    const std::string extension_id =
71e6e8a0bd7cffcc9ae2e0e75546fb12a19213d4aeBen Cheng        it->second->GetFileSystemInfo().extension_id();
72e6e8a0bd7cffcc9ae2e0e75546fb12a19213d4aeBen Cheng    ++it;
73e6e8a0bd7cffcc9ae2e0e75546fb12a19213d4aeBen Cheng    const bool unmount_result = UnmountFileSystem(
74e6e8a0bd7cffcc9ae2e0e75546fb12a19213d4aeBen Cheng        extension_id, file_system_id, UNMOUNT_REASON_SHUTDOWN);
75e6e8a0bd7cffcc9ae2e0e75546fb12a19213d4aeBen Cheng    DCHECK(unmount_result);
76e6e8a0bd7cffcc9ae2e0e75546fb12a19213d4aeBen Cheng  }
77e6e8a0bd7cffcc9ae2e0e75546fb12a19213d4aeBen Cheng
78e6e8a0bd7cffcc9ae2e0e75546fb12a19213d4aeBen Cheng  DCHECK_EQ(0u, file_system_map_.size());
79e6e8a0bd7cffcc9ae2e0e75546fb12a19213d4aeBen Cheng  STLDeleteValues(&file_system_map_);
80e6e8a0bd7cffcc9ae2e0e75546fb12a19213d4aeBen Cheng}
81e6e8a0bd7cffcc9ae2e0e75546fb12a19213d4aeBen Cheng
82e6e8a0bd7cffcc9ae2e0e75546fb12a19213d4aeBen Cheng// static
83e6e8a0bd7cffcc9ae2e0e75546fb12a19213d4aeBen ChengService* Service::Get(content::BrowserContext* context) {
84e6e8a0bd7cffcc9ae2e0e75546fb12a19213d4aeBen Cheng  return ServiceFactory::Get(context);
85e6e8a0bd7cffcc9ae2e0e75546fb12a19213d4aeBen Cheng}
86e6e8a0bd7cffcc9ae2e0e75546fb12a19213d4aeBen Cheng
87e6e8a0bd7cffcc9ae2e0e75546fb12a19213d4aeBen Chengvoid Service::AddObserver(Observer* observer) {
88e6e8a0bd7cffcc9ae2e0e75546fb12a19213d4aeBen Cheng  DCHECK(observer);
89e6e8a0bd7cffcc9ae2e0e75546fb12a19213d4aeBen Cheng  observers_.AddObserver(observer);
90e6e8a0bd7cffcc9ae2e0e75546fb12a19213d4aeBen Cheng}
91e6e8a0bd7cffcc9ae2e0e75546fb12a19213d4aeBen Cheng
92e6e8a0bd7cffcc9ae2e0e75546fb12a19213d4aeBen Chengvoid Service::RemoveObserver(Observer* observer) {
93e6e8a0bd7cffcc9ae2e0e75546fb12a19213d4aeBen Cheng  DCHECK(observer);
94e6e8a0bd7cffcc9ae2e0e75546fb12a19213d4aeBen Cheng  observers_.RemoveObserver(observer);
95e6e8a0bd7cffcc9ae2e0e75546fb12a19213d4aeBen Cheng}
96e6e8a0bd7cffcc9ae2e0e75546fb12a19213d4aeBen Cheng
97e6e8a0bd7cffcc9ae2e0e75546fb12a19213d4aeBen Chengvoid Service::SetFileSystemFactoryForTesting(
98e6e8a0bd7cffcc9ae2e0e75546fb12a19213d4aeBen Cheng    const FileSystemFactoryCallback& factory_callback) {
99e6e8a0bd7cffcc9ae2e0e75546fb12a19213d4aeBen Cheng  DCHECK(!factory_callback.is_null());
100e6e8a0bd7cffcc9ae2e0e75546fb12a19213d4aeBen Cheng  file_system_factory_ = factory_callback;
101e6e8a0bd7cffcc9ae2e0e75546fb12a19213d4aeBen Cheng}
102e6e8a0bd7cffcc9ae2e0e75546fb12a19213d4aeBen Cheng
103e6e8a0bd7cffcc9ae2e0e75546fb12a19213d4aeBen Chengbool Service::MountFileSystem(const std::string& extension_id,
104e6e8a0bd7cffcc9ae2e0e75546fb12a19213d4aeBen Cheng                              const std::string& file_system_id,
105e6e8a0bd7cffcc9ae2e0e75546fb12a19213d4aeBen Cheng                              const std::string& display_name,
106e6e8a0bd7cffcc9ae2e0e75546fb12a19213d4aeBen Cheng                              bool writable) {
107e6e8a0bd7cffcc9ae2e0e75546fb12a19213d4aeBen Cheng  DCHECK(thread_checker_.CalledOnValidThread());
108e6e8a0bd7cffcc9ae2e0e75546fb12a19213d4aeBen Cheng
109e6e8a0bd7cffcc9ae2e0e75546fb12a19213d4aeBen Cheng  // If already exists a file system provided by the same extension with this
110e6e8a0bd7cffcc9ae2e0e75546fb12a19213d4aeBen Cheng  // id, then abort.
111e6e8a0bd7cffcc9ae2e0e75546fb12a19213d4aeBen Cheng  if (GetProvidedFileSystem(extension_id, file_system_id)) {
112e6e8a0bd7cffcc9ae2e0e75546fb12a19213d4aeBen Cheng    FOR_EACH_OBSERVER(Observer,
113e6e8a0bd7cffcc9ae2e0e75546fb12a19213d4aeBen Cheng                      observers_,
114e6e8a0bd7cffcc9ae2e0e75546fb12a19213d4aeBen Cheng                      OnProvidedFileSystemMount(ProvidedFileSystemInfo(),
115e6e8a0bd7cffcc9ae2e0e75546fb12a19213d4aeBen Cheng                                                base::File::FILE_ERROR_EXISTS));
116e6e8a0bd7cffcc9ae2e0e75546fb12a19213d4aeBen Cheng    return false;
117e6e8a0bd7cffcc9ae2e0e75546fb12a19213d4aeBen Cheng  }
118e6e8a0bd7cffcc9ae2e0e75546fb12a19213d4aeBen Cheng
119e6e8a0bd7cffcc9ae2e0e75546fb12a19213d4aeBen Cheng  // Restrict number of file systems to prevent system abusing.
120e6e8a0bd7cffcc9ae2e0e75546fb12a19213d4aeBen Cheng  if (file_system_map_.size() + 1 > kMaxFileSystems) {
121e6e8a0bd7cffcc9ae2e0e75546fb12a19213d4aeBen Cheng    FOR_EACH_OBSERVER(
122e6e8a0bd7cffcc9ae2e0e75546fb12a19213d4aeBen Cheng        Observer,
123e6e8a0bd7cffcc9ae2e0e75546fb12a19213d4aeBen Cheng        observers_,
124e6e8a0bd7cffcc9ae2e0e75546fb12a19213d4aeBen Cheng        OnProvidedFileSystemMount(ProvidedFileSystemInfo(),
125e6e8a0bd7cffcc9ae2e0e75546fb12a19213d4aeBen Cheng                                  base::File::FILE_ERROR_TOO_MANY_OPENED));
126e6e8a0bd7cffcc9ae2e0e75546fb12a19213d4aeBen Cheng    return false;
127e6e8a0bd7cffcc9ae2e0e75546fb12a19213d4aeBen Cheng  }
128e6e8a0bd7cffcc9ae2e0e75546fb12a19213d4aeBen Cheng
129e6e8a0bd7cffcc9ae2e0e75546fb12a19213d4aeBen Cheng  storage::ExternalMountPoints* const mount_points =
130e6e8a0bd7cffcc9ae2e0e75546fb12a19213d4aeBen Cheng      storage::ExternalMountPoints::GetSystemInstance();
131e6e8a0bd7cffcc9ae2e0e75546fb12a19213d4aeBen Cheng  DCHECK(mount_points);
132e6e8a0bd7cffcc9ae2e0e75546fb12a19213d4aeBen Cheng
133e6e8a0bd7cffcc9ae2e0e75546fb12a19213d4aeBen Cheng  // The mount point path and name are unique per system, since they are system
134e6e8a0bd7cffcc9ae2e0e75546fb12a19213d4aeBen Cheng  // wide. This is necessary for copying between profiles.
135e6e8a0bd7cffcc9ae2e0e75546fb12a19213d4aeBen Cheng  const base::FilePath& mount_path =
136e6e8a0bd7cffcc9ae2e0e75546fb12a19213d4aeBen Cheng      util::GetMountPath(profile_, extension_id, file_system_id);
137e6e8a0bd7cffcc9ae2e0e75546fb12a19213d4aeBen Cheng  const std::string mount_point_name = mount_path.BaseName().AsUTF8Unsafe();
138e6e8a0bd7cffcc9ae2e0e75546fb12a19213d4aeBen Cheng
139e6e8a0bd7cffcc9ae2e0e75546fb12a19213d4aeBen Cheng  if (!mount_points->RegisterFileSystem(mount_point_name,
140e6e8a0bd7cffcc9ae2e0e75546fb12a19213d4aeBen Cheng                                        storage::kFileSystemTypeProvided,
141e6e8a0bd7cffcc9ae2e0e75546fb12a19213d4aeBen Cheng                                        storage::FileSystemMountOption(),
142e6e8a0bd7cffcc9ae2e0e75546fb12a19213d4aeBen Cheng                                        mount_path)) {
143e6e8a0bd7cffcc9ae2e0e75546fb12a19213d4aeBen Cheng    FOR_EACH_OBSERVER(
144e6e8a0bd7cffcc9ae2e0e75546fb12a19213d4aeBen Cheng        Observer,
145e6e8a0bd7cffcc9ae2e0e75546fb12a19213d4aeBen Cheng        observers_,
146e6e8a0bd7cffcc9ae2e0e75546fb12a19213d4aeBen Cheng        OnProvidedFileSystemMount(ProvidedFileSystemInfo(),
147e6e8a0bd7cffcc9ae2e0e75546fb12a19213d4aeBen Cheng                                  base::File::FILE_ERROR_INVALID_OPERATION));
148e6e8a0bd7cffcc9ae2e0e75546fb12a19213d4aeBen Cheng    return false;
149e6e8a0bd7cffcc9ae2e0e75546fb12a19213d4aeBen Cheng  }
150e6e8a0bd7cffcc9ae2e0e75546fb12a19213d4aeBen Cheng
151e6e8a0bd7cffcc9ae2e0e75546fb12a19213d4aeBen Cheng  // Store the file system descriptor. Use the mount point name as the file
152e6e8a0bd7cffcc9ae2e0e75546fb12a19213d4aeBen Cheng  // system provider file system id.
153e6e8a0bd7cffcc9ae2e0e75546fb12a19213d4aeBen Cheng  // Examples:
154e6e8a0bd7cffcc9ae2e0e75546fb12a19213d4aeBen Cheng  //   file_system_id = hello_world
155e6e8a0bd7cffcc9ae2e0e75546fb12a19213d4aeBen Cheng  //   mount_point_name =  b33f1337-hello_world-5aa5
156e6e8a0bd7cffcc9ae2e0e75546fb12a19213d4aeBen Cheng  //   writable = false
157e6e8a0bd7cffcc9ae2e0e75546fb12a19213d4aeBen Cheng  //   mount_path = /provided/b33f1337-hello_world-5aa5
158e6e8a0bd7cffcc9ae2e0e75546fb12a19213d4aeBen Cheng  ProvidedFileSystemInfo file_system_info(
159e6e8a0bd7cffcc9ae2e0e75546fb12a19213d4aeBen Cheng      extension_id, file_system_id, display_name, writable, mount_path);
160e6e8a0bd7cffcc9ae2e0e75546fb12a19213d4aeBen Cheng
161e6e8a0bd7cffcc9ae2e0e75546fb12a19213d4aeBen Cheng  ProvidedFileSystemInterface* file_system =
162e6e8a0bd7cffcc9ae2e0e75546fb12a19213d4aeBen Cheng      file_system_factory_.Run(profile_, file_system_info);
163e6e8a0bd7cffcc9ae2e0e75546fb12a19213d4aeBen Cheng  DCHECK(file_system);
164e6e8a0bd7cffcc9ae2e0e75546fb12a19213d4aeBen Cheng  file_system_map_[FileSystemKey(extension_id, file_system_id)] = file_system;
165e6e8a0bd7cffcc9ae2e0e75546fb12a19213d4aeBen Cheng  mount_point_name_to_key_map_[mount_point_name] =
166e6e8a0bd7cffcc9ae2e0e75546fb12a19213d4aeBen Cheng      FileSystemKey(extension_id, file_system_id);
167e6e8a0bd7cffcc9ae2e0e75546fb12a19213d4aeBen Cheng  RememberFileSystem(file_system_info);
168e6e8a0bd7cffcc9ae2e0e75546fb12a19213d4aeBen Cheng
169e6e8a0bd7cffcc9ae2e0e75546fb12a19213d4aeBen Cheng  FOR_EACH_OBSERVER(
170e6e8a0bd7cffcc9ae2e0e75546fb12a19213d4aeBen Cheng      Observer,
171e6e8a0bd7cffcc9ae2e0e75546fb12a19213d4aeBen Cheng      observers_,
172e6e8a0bd7cffcc9ae2e0e75546fb12a19213d4aeBen Cheng      OnProvidedFileSystemMount(file_system_info, base::File::FILE_OK));
173e6e8a0bd7cffcc9ae2e0e75546fb12a19213d4aeBen Cheng
174e6e8a0bd7cffcc9ae2e0e75546fb12a19213d4aeBen Cheng  return true;
175e6e8a0bd7cffcc9ae2e0e75546fb12a19213d4aeBen Cheng}
176e6e8a0bd7cffcc9ae2e0e75546fb12a19213d4aeBen Cheng
177e6e8a0bd7cffcc9ae2e0e75546fb12a19213d4aeBen Chengbool Service::UnmountFileSystem(const std::string& extension_id,
178e6e8a0bd7cffcc9ae2e0e75546fb12a19213d4aeBen Cheng                                const std::string& file_system_id,
179e6e8a0bd7cffcc9ae2e0e75546fb12a19213d4aeBen Cheng                                UnmountReason reason) {
180e6e8a0bd7cffcc9ae2e0e75546fb12a19213d4aeBen Cheng  DCHECK(thread_checker_.CalledOnValidThread());
181e6e8a0bd7cffcc9ae2e0e75546fb12a19213d4aeBen Cheng
182e6e8a0bd7cffcc9ae2e0e75546fb12a19213d4aeBen Cheng  const ProvidedFileSystemMap::iterator file_system_it =
183e6e8a0bd7cffcc9ae2e0e75546fb12a19213d4aeBen Cheng      file_system_map_.find(FileSystemKey(extension_id, file_system_id));
184e6e8a0bd7cffcc9ae2e0e75546fb12a19213d4aeBen Cheng  if (file_system_it == file_system_map_.end()) {
185e6e8a0bd7cffcc9ae2e0e75546fb12a19213d4aeBen Cheng    const ProvidedFileSystemInfo empty_file_system_info;
186e6e8a0bd7cffcc9ae2e0e75546fb12a19213d4aeBen Cheng    FOR_EACH_OBSERVER(
187e6e8a0bd7cffcc9ae2e0e75546fb12a19213d4aeBen Cheng        Observer,
188e6e8a0bd7cffcc9ae2e0e75546fb12a19213d4aeBen Cheng        observers_,
189e6e8a0bd7cffcc9ae2e0e75546fb12a19213d4aeBen Cheng        OnProvidedFileSystemUnmount(empty_file_system_info,
190                                    base::File::FILE_ERROR_NOT_FOUND));
191    return false;
192  }
193
194  storage::ExternalMountPoints* const mount_points =
195      storage::ExternalMountPoints::GetSystemInstance();
196  DCHECK(mount_points);
197
198  const ProvidedFileSystemInfo& file_system_info =
199      file_system_it->second->GetFileSystemInfo();
200
201  const std::string mount_point_name =
202      file_system_info.mount_path().BaseName().value();
203  if (!mount_points->RevokeFileSystem(mount_point_name)) {
204    FOR_EACH_OBSERVER(
205        Observer,
206        observers_,
207        OnProvidedFileSystemUnmount(file_system_info,
208                                    base::File::FILE_ERROR_INVALID_OPERATION));
209    return false;
210  }
211
212  FOR_EACH_OBSERVER(
213      Observer,
214      observers_,
215      OnProvidedFileSystemUnmount(file_system_info, base::File::FILE_OK));
216
217  mount_point_name_to_key_map_.erase(mount_point_name);
218
219  if (reason == UNMOUNT_REASON_USER) {
220    ForgetFileSystem(file_system_info.extension_id(),
221                     file_system_info.file_system_id());
222  }
223
224  delete file_system_it->second;
225  file_system_map_.erase(file_system_it);
226
227  return true;
228}
229
230bool Service::RequestUnmount(const std::string& extension_id,
231                             const std::string& file_system_id) {
232  DCHECK(thread_checker_.CalledOnValidThread());
233
234  ProvidedFileSystemMap::iterator file_system_it =
235      file_system_map_.find(FileSystemKey(extension_id, file_system_id));
236  if (file_system_it == file_system_map_.end())
237    return false;
238
239  file_system_it->second->RequestUnmount(
240      base::Bind(&Service::OnRequestUnmountStatus,
241                 weak_ptr_factory_.GetWeakPtr(),
242                 file_system_it->second->GetFileSystemInfo()));
243  return true;
244}
245
246std::vector<ProvidedFileSystemInfo> Service::GetProvidedFileSystemInfoList() {
247  DCHECK(thread_checker_.CalledOnValidThread());
248
249  std::vector<ProvidedFileSystemInfo> result;
250  for (ProvidedFileSystemMap::const_iterator it = file_system_map_.begin();
251       it != file_system_map_.end();
252       ++it) {
253    result.push_back(it->second->GetFileSystemInfo());
254  }
255  return result;
256}
257
258ProvidedFileSystemInterface* Service::GetProvidedFileSystem(
259    const std::string& extension_id,
260    const std::string& file_system_id) {
261  DCHECK(thread_checker_.CalledOnValidThread());
262
263  const ProvidedFileSystemMap::const_iterator file_system_it =
264      file_system_map_.find(FileSystemKey(extension_id, file_system_id));
265  if (file_system_it == file_system_map_.end())
266    return NULL;
267
268  return file_system_it->second;
269}
270
271void Service::OnExtensionUnloaded(
272    content::BrowserContext* browser_context,
273    const extensions::Extension* extension,
274    extensions::UnloadedExtensionInfo::Reason reason) {
275  // Unmount all of the provided file systems associated with this extension.
276  ProvidedFileSystemMap::iterator it = file_system_map_.begin();
277  while (it != file_system_map_.end()) {
278    const ProvidedFileSystemInfo& file_system_info =
279        it->second->GetFileSystemInfo();
280    // Advance the iterator beforehand, otherwise it will become invalidated
281    // by the UnmountFileSystem() call.
282    ++it;
283    if (file_system_info.extension_id() == extension->id()) {
284      const bool unmount_result = UnmountFileSystem(
285          file_system_info.extension_id(),
286          file_system_info.file_system_id(),
287          reason == extensions::UnloadedExtensionInfo::REASON_PROFILE_SHUTDOWN
288              ? UNMOUNT_REASON_SHUTDOWN
289              : UNMOUNT_REASON_USER);
290      DCHECK(unmount_result);
291    }
292  }
293}
294
295void Service::OnExtensionLoaded(content::BrowserContext* browser_context,
296                                const extensions::Extension* extension) {
297  RestoreFileSystems(extension->id());
298}
299
300ProvidedFileSystemInterface* Service::GetProvidedFileSystem(
301    const std::string& mount_point_name) {
302  DCHECK(thread_checker_.CalledOnValidThread());
303
304  const MountPointNameToKeyMap::const_iterator mapping_it =
305      mount_point_name_to_key_map_.find(mount_point_name);
306  if (mapping_it == mount_point_name_to_key_map_.end())
307    return NULL;
308
309  const ProvidedFileSystemMap::const_iterator file_system_it =
310      file_system_map_.find(mapping_it->second);
311  if (file_system_it == file_system_map_.end())
312    return NULL;
313
314  return file_system_it->second;
315}
316
317void Service::OnRequestUnmountStatus(
318    const ProvidedFileSystemInfo& file_system_info,
319    base::File::Error error) {
320  // Notify observers about failure in unmounting, since mount() will not be
321  // called by the provided file system. In case of success mount() will be
322  // invoked, and observers notified, so there is no need to call them now.
323  if (error != base::File::FILE_OK) {
324    FOR_EACH_OBSERVER(Observer,
325                      observers_,
326                      OnProvidedFileSystemUnmount(file_system_info, error));
327  }
328}
329
330void Service::RememberFileSystem(
331    const ProvidedFileSystemInfo& file_system_info) {
332  base::DictionaryValue* file_system = new base::DictionaryValue();
333  file_system->SetStringWithoutPathExpansion(kPrefKeyFileSystemId,
334                                             file_system_info.file_system_id());
335  file_system->SetStringWithoutPathExpansion(kPrefKeyDisplayName,
336                                             file_system_info.display_name());
337  file_system->SetBooleanWithoutPathExpansion(kPrefKeyWritable,
338                                              file_system_info.writable());
339
340  PrefService* const pref_service = profile_->GetPrefs();
341  DCHECK(pref_service);
342
343  DictionaryPrefUpdate dict_update(pref_service,
344                                   prefs::kFileSystemProviderMounted);
345
346  base::DictionaryValue* file_systems_per_extension = NULL;
347  if (!dict_update->GetDictionaryWithoutPathExpansion(
348          file_system_info.extension_id(), &file_systems_per_extension)) {
349    file_systems_per_extension = new base::DictionaryValue();
350    dict_update->SetWithoutPathExpansion(file_system_info.extension_id(),
351                                         file_systems_per_extension);
352  }
353
354  file_systems_per_extension->SetWithoutPathExpansion(
355      file_system_info.file_system_id(), file_system);
356}
357
358void Service::ForgetFileSystem(const std::string& extension_id,
359                               const std::string& file_system_id) {
360  PrefService* const pref_service = profile_->GetPrefs();
361  DCHECK(pref_service);
362
363  DictionaryPrefUpdate dict_update(pref_service,
364                                   prefs::kFileSystemProviderMounted);
365
366  base::DictionaryValue* file_systems_per_extension = NULL;
367  if (!dict_update->GetDictionaryWithoutPathExpansion(
368          extension_id, &file_systems_per_extension))
369    return;  // Nothing to forget.
370
371  file_systems_per_extension->RemoveWithoutPathExpansion(file_system_id, NULL);
372  if (!file_systems_per_extension->size())
373    dict_update->Remove(extension_id, NULL);
374}
375
376void Service::RestoreFileSystems(const std::string& extension_id) {
377  PrefService* const pref_service = profile_->GetPrefs();
378  DCHECK(pref_service);
379
380  const base::DictionaryValue* const file_systems =
381      pref_service->GetDictionary(prefs::kFileSystemProviderMounted);
382  DCHECK(file_systems);
383
384  const base::DictionaryValue* file_systems_per_extension = NULL;
385  if (!file_systems->GetDictionaryWithoutPathExpansion(
386          extension_id, &file_systems_per_extension))
387    return;  // Nothing to restore.
388
389  // Use a copy of the dictionary, since the original one may be modified while
390  // iterating over it.
391  scoped_ptr<const base::DictionaryValue> file_systems_per_extension_copy(
392      file_systems_per_extension->DeepCopy());
393
394  for (base::DictionaryValue::Iterator it(*file_systems_per_extension_copy);
395       !it.IsAtEnd();
396       it.Advance()) {
397    const base::Value* file_system_value = NULL;
398    const base::DictionaryValue* file_system = NULL;
399    file_systems_per_extension_copy->GetWithoutPathExpansion(
400        it.key(), &file_system_value);
401    DCHECK(file_system_value);
402
403    std::string file_system_id;
404    std::string display_name;
405    bool writable;
406
407    if (!file_system_value->GetAsDictionary(&file_system) ||
408        !file_system->GetStringWithoutPathExpansion(kPrefKeyFileSystemId,
409                                                    &file_system_id) ||
410        !file_system->GetStringWithoutPathExpansion(kPrefKeyDisplayName,
411                                                    &display_name) ||
412        !file_system->GetBooleanWithoutPathExpansion(kPrefKeyWritable,
413                                                     &writable) ||
414        file_system_id.empty() || display_name.empty()) {
415      LOG(ERROR)
416          << "Malformed provided file system information in preferences.";
417      continue;
418    }
419
420    const bool result =
421        MountFileSystem(extension_id, file_system_id, display_name, writable);
422    if (!result) {
423      LOG(ERROR) << "Failed to restore a provided file system from "
424                 << "preferences: " << extension_id << ", " << file_system_id
425                 << ", " << display_name << ".";
426      // Since remounting of the file system failed, then remove it from
427      // preferences to avoid remounting it over and over again with a failure.
428      ForgetFileSystem(extension_id, file_system_id);
429    }
430  }
431}
432
433}  // namespace file_system_provider
434}  // namespace chromeos
435