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/registry.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/service_factory.h"
16 #include "chrome/browser/profiles/profile.h"
17 #include "chrome/common/pref_names.h"
18 #include "components/pref_registry/pref_registry_syncable.h"
19 #include "extensions/browser/extension_registry.h"
20 #include "extensions/browser/extension_system.h"
21 #include "storage/browser/fileapi/external_mount_points.h"
24 namespace file_system_provider
{
26 const char kPrefKeyFileSystemId
[] = "file-system-id";
27 const char kPrefKeyDisplayName
[] = "display-name";
28 const char kPrefKeyWritable
[] = "writable";
29 const char kPrefKeySupportsNotifyTag
[] = "supports-notify-tag";
30 const char kPrefKeyWatchers
[] = "watchers";
31 const char kPrefKeyWatcherEntryPath
[] = "entry-path";
32 const char kPrefKeyWatcherRecursive
[] = "recursive";
33 const char kPrefKeyWatcherLastTag
[] = "last-tag";
34 const char kPrefKeyWatcherPersistentOrigins
[] = "persistent-origins";
35 const char kPrefKeyOpenedFilesLimit
[] = "opened-files-limit";
37 void RegisterProfilePrefs(user_prefs::PrefRegistrySyncable
* registry
) {
38 registry
->RegisterDictionaryPref(prefs::kFileSystemProviderMounted
);
41 Registry::Registry(Profile
* profile
) : profile_(profile
) {
44 Registry::~Registry() {
47 void Registry::RememberFileSystem(
48 const ProvidedFileSystemInfo
& file_system_info
,
49 const Watchers
& watchers
) {
50 base::DictionaryValue
* const file_system
= new base::DictionaryValue();
51 file_system
->SetStringWithoutPathExpansion(kPrefKeyFileSystemId
,
52 file_system_info
.file_system_id());
53 file_system
->SetStringWithoutPathExpansion(kPrefKeyDisplayName
,
54 file_system_info
.display_name());
55 file_system
->SetBooleanWithoutPathExpansion(kPrefKeyWritable
,
56 file_system_info
.writable());
57 file_system
->SetBooleanWithoutPathExpansion(
58 kPrefKeySupportsNotifyTag
, file_system_info
.supports_notify_tag());
59 file_system
->SetIntegerWithoutPathExpansion(
60 kPrefKeyOpenedFilesLimit
, file_system_info
.opened_files_limit());
62 base::DictionaryValue
* const watchers_value
= new base::DictionaryValue();
63 file_system
->SetWithoutPathExpansion(kPrefKeyWatchers
, watchers_value
);
65 for (const auto& it
: watchers
) {
66 base::DictionaryValue
* const watcher
= new base::DictionaryValue();
67 watchers_value
->SetWithoutPathExpansion(it
.second
.entry_path
.value(),
69 watcher
->SetStringWithoutPathExpansion(kPrefKeyWatcherEntryPath
,
70 it
.second
.entry_path
.value());
71 watcher
->SetBooleanWithoutPathExpansion(kPrefKeyWatcherRecursive
,
73 watcher
->SetStringWithoutPathExpansion(kPrefKeyWatcherLastTag
,
75 base::ListValue
* const persistent_origins_value
= new base::ListValue();
76 watcher
->SetWithoutPathExpansion(kPrefKeyWatcherPersistentOrigins
,
77 persistent_origins_value
);
78 for (const auto& subscriber_it
: it
.second
.subscribers
) {
79 // Only persistent subscribers should be stored in persistent storage.
80 // Other ones should not be restired after a restart.
81 if (subscriber_it
.second
.persistent
)
82 persistent_origins_value
->AppendString(subscriber_it
.first
.spec());
86 PrefService
* const pref_service
= profile_
->GetPrefs();
89 DictionaryPrefUpdate
dict_update(pref_service
,
90 prefs::kFileSystemProviderMounted
);
92 base::DictionaryValue
* file_systems_per_extension
= NULL
;
93 if (!dict_update
->GetDictionaryWithoutPathExpansion(
94 file_system_info
.extension_id(), &file_systems_per_extension
)) {
95 file_systems_per_extension
= new base::DictionaryValue();
96 dict_update
->SetWithoutPathExpansion(file_system_info
.extension_id(),
97 file_systems_per_extension
);
100 file_systems_per_extension
->SetWithoutPathExpansion(
101 file_system_info
.file_system_id(), file_system
);
104 void Registry::ForgetFileSystem(const std::string
& extension_id
,
105 const std::string
& file_system_id
) {
106 PrefService
* const pref_service
= profile_
->GetPrefs();
107 DCHECK(pref_service
);
109 DictionaryPrefUpdate
dict_update(pref_service
,
110 prefs::kFileSystemProviderMounted
);
112 base::DictionaryValue
* file_systems_per_extension
= NULL
;
113 if (!dict_update
->GetDictionaryWithoutPathExpansion(
114 extension_id
, &file_systems_per_extension
))
115 return; // Nothing to forget.
117 file_systems_per_extension
->RemoveWithoutPathExpansion(file_system_id
, NULL
);
118 if (!file_systems_per_extension
->size())
119 dict_update
->Remove(extension_id
, NULL
);
122 scoped_ptr
<Registry::RestoredFileSystems
> Registry::RestoreFileSystems(
123 const std::string
& extension_id
) {
124 PrefService
* const pref_service
= profile_
->GetPrefs();
125 DCHECK(pref_service
);
127 const base::DictionaryValue
* const file_systems
=
128 pref_service
->GetDictionary(prefs::kFileSystemProviderMounted
);
129 DCHECK(file_systems
);
131 const base::DictionaryValue
* file_systems_per_extension
= NULL
;
132 if (!file_systems
->GetDictionaryWithoutPathExpansion(
133 extension_id
, &file_systems_per_extension
)) {
134 return make_scoped_ptr(new RestoredFileSystems
); // Nothing to restore.
137 scoped_ptr
<RestoredFileSystems
> restored_file_systems(
138 new RestoredFileSystems
);
140 for (base::DictionaryValue::Iterator
it(*file_systems_per_extension
);
143 const base::Value
* file_system_value
= NULL
;
144 const base::DictionaryValue
* file_system
= NULL
;
145 file_systems_per_extension
->GetWithoutPathExpansion(it
.key(),
147 DCHECK(file_system_value
);
149 std::string file_system_id
;
150 std::string display_name
;
151 bool writable
= false;
152 bool supports_notify_tag
= false;
153 int opened_files_limit
= 0;
155 // TODO(mtomasz): Move opened files limit to the mandatory list above in
157 if ((!file_system_value
->GetAsDictionary(&file_system
) ||
158 !file_system
->GetStringWithoutPathExpansion(kPrefKeyFileSystemId
,
160 !file_system
->GetStringWithoutPathExpansion(kPrefKeyDisplayName
,
162 !file_system
->GetBooleanWithoutPathExpansion(kPrefKeyWritable
,
164 !file_system
->GetBooleanWithoutPathExpansion(kPrefKeySupportsNotifyTag
,
165 &supports_notify_tag
) ||
166 file_system_id
.empty() || display_name
.empty()) ||
168 (file_system
->GetIntegerWithoutPathExpansion(kPrefKeyOpenedFilesLimit
,
169 &opened_files_limit
) &&
170 opened_files_limit
< 0)) {
172 << "Malformed provided file system information in preferences.";
176 MountOptions options
;
177 options
.file_system_id
= file_system_id
;
178 options
.display_name
= display_name
;
179 options
.writable
= writable
;
180 options
.supports_notify_tag
= supports_notify_tag
;
181 options
.opened_files_limit
= opened_files_limit
;
183 RestoredFileSystem restored_file_system
;
184 restored_file_system
.extension_id
= extension_id
;
185 restored_file_system
.options
= options
;
187 // Restore watchers. It's optional, since this field is new.
188 const base::DictionaryValue
* watchers
= NULL
;
189 if (file_system
->GetDictionaryWithoutPathExpansion(kPrefKeyWatchers
,
191 for (base::DictionaryValue::Iterator
it(*watchers
); !it
.IsAtEnd();
193 const base::Value
* watcher_value
= NULL
;
194 const base::DictionaryValue
* watcher
= NULL
;
195 watchers
->GetWithoutPathExpansion(it
.key(), &watcher_value
);
196 DCHECK(file_system_value
);
198 std::string entry_path
;
199 bool recursive
= false;
200 std::string last_tag
;
201 const base::ListValue
* persistent_origins
= NULL
;
203 if (!watcher_value
->GetAsDictionary(&watcher
) ||
204 !watcher
->GetStringWithoutPathExpansion(kPrefKeyWatcherEntryPath
,
206 !watcher
->GetBooleanWithoutPathExpansion(kPrefKeyWatcherRecursive
,
208 !watcher
->GetStringWithoutPathExpansion(kPrefKeyWatcherLastTag
,
210 !watcher
->GetListWithoutPathExpansion(
211 kPrefKeyWatcherPersistentOrigins
, &persistent_origins
) ||
212 it
.key() != entry_path
|| entry_path
.empty() ||
213 (!options
.supports_notify_tag
&&
214 (!last_tag
.empty() || persistent_origins
->GetSize()))) {
215 LOG(ERROR
) << "Malformed watcher information in preferences.";
219 Watcher restored_watcher
;
220 restored_watcher
.entry_path
=
221 base::FilePath::FromUTF8Unsafe(entry_path
);
222 restored_watcher
.recursive
= recursive
;
223 restored_watcher
.last_tag
= last_tag
;
224 for (const auto& persistent_origin
: *persistent_origins
) {
226 if (persistent_origin
->GetAsString(&origin
)) {
227 LOG(ERROR
) << "Malformed subscriber information in preferences.";
230 const GURL
origin_as_gurl(origin
);
231 restored_watcher
.subscribers
[origin_as_gurl
].origin
= origin_as_gurl
;
232 restored_watcher
.subscribers
[origin_as_gurl
].persistent
= true;
234 restored_file_system
.watchers
[WatcherKey(
235 base::FilePath::FromUTF8Unsafe(entry_path
), recursive
)] =
239 restored_file_systems
->push_back(restored_file_system
);
242 return restored_file_systems
.Pass();
245 void Registry::UpdateWatcherTag(const ProvidedFileSystemInfo
& file_system_info
,
246 const Watcher
& watcher
) {
247 PrefService
* const pref_service
= profile_
->GetPrefs();
248 DCHECK(pref_service
);
250 // TODO(mtomasz): Consider optimizing it by moving information about watchers
251 // or even file systems to leveldb.
252 DictionaryPrefUpdate
dict_update(pref_service
,
253 prefs::kFileSystemProviderMounted
);
255 // All of the following checks should not happen in healthy environment.
256 // However, since they rely on storage, DCHECKs can't be used.
257 base::DictionaryValue
* file_systems_per_extension
= NULL
;
258 base::DictionaryValue
* file_system
= NULL
;
259 base::DictionaryValue
* watchers
= NULL
;
260 base::DictionaryValue
* watcher_value
= NULL
;
261 if (!dict_update
->GetDictionaryWithoutPathExpansion(
262 file_system_info
.extension_id(), &file_systems_per_extension
) ||
263 !file_systems_per_extension
->GetDictionaryWithoutPathExpansion(
264 file_system_info
.file_system_id(), &file_system
) ||
265 !file_system
->GetDictionaryWithoutPathExpansion(kPrefKeyWatchers
,
267 !watchers
->GetDictionaryWithoutPathExpansion(watcher
.entry_path
.value(),
269 // Broken preferences.
270 LOG(ERROR
) << "Broken preferences detected while updating a tag.";
274 watcher_value
->SetStringWithoutPathExpansion(kPrefKeyWatcherLastTag
,
278 } // namespace file_system_provider
279 } // namespace chromeos