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(
39 prefs::kFileSystemProviderMounted
,
40 user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF
);
43 Registry::Registry(Profile
* profile
) : profile_(profile
) {
46 Registry::~Registry() {
49 void Registry::RememberFileSystem(
50 const ProvidedFileSystemInfo
& file_system_info
,
51 const Watchers
& watchers
) {
52 base::DictionaryValue
* const file_system
= new base::DictionaryValue();
53 file_system
->SetStringWithoutPathExpansion(kPrefKeyFileSystemId
,
54 file_system_info
.file_system_id());
55 file_system
->SetStringWithoutPathExpansion(kPrefKeyDisplayName
,
56 file_system_info
.display_name());
57 file_system
->SetBooleanWithoutPathExpansion(kPrefKeyWritable
,
58 file_system_info
.writable());
59 file_system
->SetBooleanWithoutPathExpansion(
60 kPrefKeySupportsNotifyTag
, file_system_info
.supports_notify_tag());
61 file_system
->SetIntegerWithoutPathExpansion(
62 kPrefKeyOpenedFilesLimit
, file_system_info
.opened_files_limit());
64 base::DictionaryValue
* const watchers_value
= new base::DictionaryValue();
65 file_system
->SetWithoutPathExpansion(kPrefKeyWatchers
, watchers_value
);
67 for (const auto& it
: watchers
) {
68 base::DictionaryValue
* const watcher
= new base::DictionaryValue();
69 watchers_value
->SetWithoutPathExpansion(it
.second
.entry_path
.value(),
71 watcher
->SetStringWithoutPathExpansion(kPrefKeyWatcherEntryPath
,
72 it
.second
.entry_path
.value());
73 watcher
->SetBooleanWithoutPathExpansion(kPrefKeyWatcherRecursive
,
75 watcher
->SetStringWithoutPathExpansion(kPrefKeyWatcherLastTag
,
77 base::ListValue
* const persistent_origins_value
= new base::ListValue();
78 watcher
->SetWithoutPathExpansion(kPrefKeyWatcherPersistentOrigins
,
79 persistent_origins_value
);
80 for (const auto& subscriber_it
: it
.second
.subscribers
) {
81 // Only persistent subscribers should be stored in persistent storage.
82 // Other ones should not be restired after a restart.
83 if (subscriber_it
.second
.persistent
)
84 persistent_origins_value
->AppendString(subscriber_it
.first
.spec());
88 PrefService
* const pref_service
= profile_
->GetPrefs();
91 DictionaryPrefUpdate
dict_update(pref_service
,
92 prefs::kFileSystemProviderMounted
);
94 base::DictionaryValue
* file_systems_per_extension
= NULL
;
95 if (!dict_update
->GetDictionaryWithoutPathExpansion(
96 file_system_info
.extension_id(), &file_systems_per_extension
)) {
97 file_systems_per_extension
= new base::DictionaryValue();
98 dict_update
->SetWithoutPathExpansion(file_system_info
.extension_id(),
99 file_systems_per_extension
);
102 file_systems_per_extension
->SetWithoutPathExpansion(
103 file_system_info
.file_system_id(), file_system
);
106 void Registry::ForgetFileSystem(const std::string
& extension_id
,
107 const std::string
& file_system_id
) {
108 PrefService
* const pref_service
= profile_
->GetPrefs();
109 DCHECK(pref_service
);
111 DictionaryPrefUpdate
dict_update(pref_service
,
112 prefs::kFileSystemProviderMounted
);
114 base::DictionaryValue
* file_systems_per_extension
= NULL
;
115 if (!dict_update
->GetDictionaryWithoutPathExpansion(
116 extension_id
, &file_systems_per_extension
))
117 return; // Nothing to forget.
119 file_systems_per_extension
->RemoveWithoutPathExpansion(file_system_id
, NULL
);
120 if (!file_systems_per_extension
->size())
121 dict_update
->Remove(extension_id
, NULL
);
124 scoped_ptr
<Registry::RestoredFileSystems
> Registry::RestoreFileSystems(
125 const std::string
& extension_id
) {
126 PrefService
* const pref_service
= profile_
->GetPrefs();
127 DCHECK(pref_service
);
129 const base::DictionaryValue
* const file_systems
=
130 pref_service
->GetDictionary(prefs::kFileSystemProviderMounted
);
131 DCHECK(file_systems
);
133 const base::DictionaryValue
* file_systems_per_extension
= NULL
;
134 if (!file_systems
->GetDictionaryWithoutPathExpansion(
135 extension_id
, &file_systems_per_extension
)) {
136 return make_scoped_ptr(new RestoredFileSystems
); // Nothing to restore.
139 scoped_ptr
<RestoredFileSystems
> restored_file_systems(
140 new RestoredFileSystems
);
142 for (base::DictionaryValue::Iterator
it(*file_systems_per_extension
);
145 const base::Value
* file_system_value
= NULL
;
146 const base::DictionaryValue
* file_system
= NULL
;
147 file_systems_per_extension
->GetWithoutPathExpansion(it
.key(),
149 DCHECK(file_system_value
);
151 std::string file_system_id
;
152 std::string display_name
;
153 bool writable
= false;
154 bool supports_notify_tag
= false;
155 int opened_files_limit
= 0;
157 // TODO(mtomasz): Move opened files limit to the mandatory list above in
159 if ((!file_system_value
->GetAsDictionary(&file_system
) ||
160 !file_system
->GetStringWithoutPathExpansion(kPrefKeyFileSystemId
,
162 !file_system
->GetStringWithoutPathExpansion(kPrefKeyDisplayName
,
164 !file_system
->GetBooleanWithoutPathExpansion(kPrefKeyWritable
,
166 !file_system
->GetBooleanWithoutPathExpansion(kPrefKeySupportsNotifyTag
,
167 &supports_notify_tag
) ||
168 file_system_id
.empty() || display_name
.empty()) ||
169 (file_system
->GetIntegerWithoutPathExpansion(kPrefKeyOpenedFilesLimit
,
170 &opened_files_limit
) &&
171 opened_files_limit
< 0)) {
173 << "Malformed provided file system information in preferences.";
177 MountOptions options
;
178 options
.file_system_id
= file_system_id
;
179 options
.display_name
= display_name
;
180 options
.writable
= writable
;
181 options
.supports_notify_tag
= supports_notify_tag
;
182 options
.opened_files_limit
= opened_files_limit
;
184 RestoredFileSystem restored_file_system
;
185 restored_file_system
.extension_id
= extension_id
;
186 restored_file_system
.options
= options
;
188 // Restore watchers. It's optional, since this field is new.
189 const base::DictionaryValue
* watchers
= NULL
;
190 if (file_system
->GetDictionaryWithoutPathExpansion(kPrefKeyWatchers
,
192 for (base::DictionaryValue::Iterator
it(*watchers
); !it
.IsAtEnd();
194 const base::Value
* watcher_value
= NULL
;
195 const base::DictionaryValue
* watcher
= NULL
;
196 watchers
->GetWithoutPathExpansion(it
.key(), &watcher_value
);
197 DCHECK(file_system_value
);
199 std::string entry_path
;
200 bool recursive
= false;
201 std::string last_tag
;
202 const base::ListValue
* persistent_origins
= NULL
;
204 if (!watcher_value
->GetAsDictionary(&watcher
) ||
205 !watcher
->GetStringWithoutPathExpansion(kPrefKeyWatcherEntryPath
,
207 !watcher
->GetBooleanWithoutPathExpansion(kPrefKeyWatcherRecursive
,
209 !watcher
->GetStringWithoutPathExpansion(kPrefKeyWatcherLastTag
,
211 !watcher
->GetListWithoutPathExpansion(
212 kPrefKeyWatcherPersistentOrigins
, &persistent_origins
) ||
213 it
.key() != entry_path
|| entry_path
.empty() ||
214 (!options
.supports_notify_tag
&&
215 (!last_tag
.empty() || persistent_origins
->GetSize()))) {
216 LOG(ERROR
) << "Malformed watcher information in preferences.";
220 Watcher restored_watcher
;
221 restored_watcher
.entry_path
=
222 base::FilePath::FromUTF8Unsafe(entry_path
);
223 restored_watcher
.recursive
= recursive
;
224 restored_watcher
.last_tag
= last_tag
;
225 for (const auto& persistent_origin
: *persistent_origins
) {
227 if (persistent_origin
->GetAsString(&origin
)) {
228 LOG(ERROR
) << "Malformed subscriber information in preferences.";
231 const GURL
origin_as_gurl(origin
);
232 restored_watcher
.subscribers
[origin_as_gurl
].origin
= origin_as_gurl
;
233 restored_watcher
.subscribers
[origin_as_gurl
].persistent
= true;
235 restored_file_system
.watchers
[WatcherKey(
236 base::FilePath::FromUTF8Unsafe(entry_path
), recursive
)] =
240 restored_file_systems
->push_back(restored_file_system
);
243 return restored_file_systems
.Pass();
246 void Registry::UpdateWatcherTag(const ProvidedFileSystemInfo
& file_system_info
,
247 const Watcher
& watcher
) {
248 PrefService
* const pref_service
= profile_
->GetPrefs();
249 DCHECK(pref_service
);
251 // TODO(mtomasz): Consider optimizing it by moving information about watchers
252 // or even file systems to leveldb.
253 DictionaryPrefUpdate
dict_update(pref_service
,
254 prefs::kFileSystemProviderMounted
);
256 // All of the following checks should not happen in healthy environment.
257 // However, since they rely on storage, DCHECKs can't be used.
258 base::DictionaryValue
* file_systems_per_extension
= NULL
;
259 base::DictionaryValue
* file_system
= NULL
;
260 base::DictionaryValue
* watchers
= NULL
;
261 base::DictionaryValue
* watcher_value
= NULL
;
262 if (!dict_update
->GetDictionaryWithoutPathExpansion(
263 file_system_info
.extension_id(), &file_systems_per_extension
) ||
264 !file_systems_per_extension
->GetDictionaryWithoutPathExpansion(
265 file_system_info
.file_system_id(), &file_system
) ||
266 !file_system
->GetDictionaryWithoutPathExpansion(kPrefKeyWatchers
,
268 !watchers
->GetDictionaryWithoutPathExpansion(watcher
.entry_path
.value(),
270 // Broken preferences.
271 LOG(ERROR
) << "Broken preferences detected while updating a tag.";
275 watcher_value
->SetStringWithoutPathExpansion(kPrefKeyWatcherLastTag
,
279 } // namespace file_system_provider
280 } // namespace chromeos