| // Copyright 2014 The Chromium Authors |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "chrome/browser/ash/file_system_provider/registry.h" |
| |
| #include <optional> |
| #include <utility> |
| |
| #include "base/files/file_path.h" |
| #include "base/memory/ptr_util.h" |
| #include "base/values.h" |
| #include "chrome/browser/ash/file_system_provider/mount_path_util.h" |
| #include "chrome/browser/ash/file_system_provider/observer.h" |
| #include "chrome/browser/ash/file_system_provider/provided_file_system.h" |
| #include "chrome/browser/ash/file_system_provider/provided_file_system_info.h" |
| #include "chrome/browser/ash/file_system_provider/service_factory.h" |
| #include "chrome/browser/profiles/profile.h" |
| #include "chrome/common/pref_names.h" |
| #include "components/pref_registry/pref_registry_syncable.h" |
| #include "components/prefs/pref_service.h" |
| #include "components/prefs/scoped_user_pref_update.h" |
| #include "extensions/browser/extension_registry.h" |
| #include "extensions/browser/extension_system.h" |
| #include "storage/browser/file_system/external_mount_points.h" |
| |
| namespace ash::file_system_provider { |
| |
| const char kPrefKeyFileSystemId[] = "file-system-id"; |
| const char kPrefKeyDisplayName[] = "display-name"; |
| const char kPrefKeyWritable[] = "writable"; |
| const char kPrefKeySupportsNotifyTag[] = "supports-notify-tag"; |
| const char kPrefKeyWatchers[] = "watchers"; |
| const char kPrefKeyWatcherEntryPath[] = "entry-path"; |
| const char kPrefKeyWatcherRecursive[] = "recursive"; |
| const char kPrefKeyWatcherLastTag[] = "last-tag"; |
| const char kPrefKeyWatcherPersistentOrigins[] = "persistent-origins"; |
| const char kPrefKeyOpenedFilesLimit[] = "opened-files-limit"; |
| |
| void RegisterProfilePrefs(user_prefs::PrefRegistrySyncable* registry) { |
| registry->RegisterDictionaryPref(prefs::kFileSystemProviderMounted); |
| } |
| |
| Registry::Registry(Profile* profile) : profile_(profile) { |
| } |
| |
| Registry::~Registry() = default; |
| |
| void Registry::RememberFileSystem( |
| const ProvidedFileSystemInfo& file_system_info, |
| const Watchers& watchers) { |
| base::Value::Dict file_system; |
| file_system.Set(kPrefKeyFileSystemId, file_system_info.file_system_id()); |
| file_system.Set(kPrefKeyDisplayName, file_system_info.display_name()); |
| file_system.Set(kPrefKeyWritable, file_system_info.writable()); |
| file_system.Set(kPrefKeySupportsNotifyTag, |
| file_system_info.supports_notify_tag()); |
| file_system.Set(kPrefKeyOpenedFilesLimit, |
| file_system_info.opened_files_limit()); |
| // We don't need to write and read "persistent" field (in MountOptions) to |
| // and from preference because all filesystems which are remembered must be |
| // persistent. |
| |
| base::Value::Dict watchers_dict; |
| |
| for (const auto& it : watchers) { |
| base::Value::Dict watcher; |
| watcher.Set(kPrefKeyWatcherEntryPath, it.second.entry_path.value()); |
| watcher.Set(kPrefKeyWatcherRecursive, it.second.recursive); |
| watcher.Set(kPrefKeyWatcherLastTag, it.second.last_tag); |
| base::Value::List persistent_origins_value; |
| for (const auto& subscriber_it : it.second.subscribers) { |
| // Only persistent subscribers should be stored in persistent storage. |
| // Other ones should not be restired after a restart. |
| if (subscriber_it.second.persistent) { |
| persistent_origins_value.Append(subscriber_it.first.spec()); |
| } |
| } |
| watcher.Set(kPrefKeyWatcherPersistentOrigins, |
| std::move(persistent_origins_value)); |
| watchers_dict.Set(it.second.entry_path.value(), std::move(watcher)); |
| } |
| file_system.Set(kPrefKeyWatchers, std::move(watchers_dict)); |
| |
| PrefService* const pref_service = profile_->GetPrefs(); |
| DCHECK(pref_service); |
| |
| ScopedDictPrefUpdate dict_update(pref_service, |
| prefs::kFileSystemProviderMounted); |
| |
| base::Value::Dict* file_systems_per_extension = |
| dict_update->EnsureDict(file_system_info.provider_id().ToString()); |
| file_systems_per_extension->Set(file_system_info.file_system_id(), |
| std::move(file_system)); |
| } |
| |
| void Registry::ForgetFileSystem(const ProviderId& provider_id, |
| const std::string& file_system_id) { |
| PrefService* const pref_service = profile_->GetPrefs(); |
| DCHECK(pref_service); |
| |
| ScopedDictPrefUpdate dict_update(pref_service, |
| prefs::kFileSystemProviderMounted); |
| |
| base::Value::Dict* file_systems_per_extension = |
| dict_update->FindDict(provider_id.ToString()); |
| if (!file_systems_per_extension) |
| return; // Nothing to forget. |
| |
| file_systems_per_extension->Remove(file_system_id); |
| if (file_systems_per_extension->empty()) |
| dict_update->Remove(provider_id.ToString()); |
| } |
| |
| std::unique_ptr<Registry::RestoredFileSystems> Registry::RestoreFileSystems( |
| const ProviderId& provider_id) { |
| PrefService* const pref_service = profile_->GetPrefs(); |
| DCHECK(pref_service); |
| |
| const base::Value::Dict& file_systems = |
| pref_service->GetDict(prefs::kFileSystemProviderMounted); |
| |
| const base::Value::Dict* file_systems_per_extension = |
| file_systems.FindDict(provider_id.ToString()); |
| if (!file_systems_per_extension) { |
| return base::WrapUnique(new RestoredFileSystems); // Nothing to restore. |
| } |
| |
| std::unique_ptr<RestoredFileSystems> restored_file_systems( |
| new RestoredFileSystems); |
| |
| for (const auto file_system_it : *file_systems_per_extension) { |
| if (!file_system_it.second.is_dict()) { |
| LOG(ERROR) |
| << "Malformed provided file system information in preferences."; |
| continue; |
| } |
| |
| const base::Value::Dict& file_system = file_system_it.second.GetDict(); |
| |
| const std::string* file_system_id = |
| file_system.FindString(kPrefKeyFileSystemId); |
| const std::string* display_name = |
| file_system.FindString(kPrefKeyDisplayName); |
| std::optional<bool> writable = file_system.FindBool(kPrefKeyWritable); |
| std::optional<bool> supports_notify_tag = |
| file_system.FindBool(kPrefKeySupportsNotifyTag); |
| std::optional<int> opened_files_limit = |
| file_system.FindInt(kPrefKeyOpenedFilesLimit); |
| |
| // TODO(mtomasz): Move opened files limit to the mandatory list above in |
| // M42. |
| if ((!file_system_id || !display_name || !writable || |
| !supports_notify_tag || file_system_id->empty() || |
| display_name->empty()) || |
| // Optional fields. |
| (opened_files_limit.has_value() && opened_files_limit.value() < 0)) { |
| LOG(ERROR) |
| << "Malformed provided file system information in preferences."; |
| continue; |
| } |
| |
| MountOptions options; |
| options.file_system_id = *file_system_id; |
| options.display_name = *display_name; |
| options.writable = writable.value(); |
| options.supports_notify_tag = supports_notify_tag.value(); |
| options.opened_files_limit = opened_files_limit.value_or(0); |
| |
| RestoredFileSystem restored_file_system; |
| restored_file_system.provider_id = provider_id; |
| restored_file_system.options = options; |
| |
| // Restore watchers. It's optional, since this field is new. |
| const base::Value::Dict* watchers = file_system.FindDict(kPrefKeyWatchers); |
| if (watchers) { |
| for (const auto watcher_it : *watchers) { |
| if (!watcher_it.second.is_dict()) { |
| LOG(ERROR) << "Malformed watcher information in preferences."; |
| continue; |
| } |
| |
| const base::Value::Dict& watcher = watcher_it.second.GetDict(); |
| |
| const std::string* entry_path = |
| watcher.FindString(kPrefKeyWatcherEntryPath); |
| std::optional<bool> recursive = |
| watcher.FindBool(kPrefKeyWatcherRecursive); |
| const std::string* last_tag = |
| watcher.FindString(kPrefKeyWatcherLastTag); |
| const base::Value::List* persistent_origins = |
| watcher.FindList(kPrefKeyWatcherPersistentOrigins); |
| |
| if (!entry_path || !recursive || !last_tag || !persistent_origins || |
| watcher_it.first != *entry_path || entry_path->empty() || |
| (!options.supports_notify_tag && |
| (!last_tag->empty() || persistent_origins->size()))) { |
| LOG(ERROR) << "Malformed watcher information in preferences."; |
| continue; |
| } |
| |
| Watcher restored_watcher; |
| restored_watcher.entry_path = |
| base::FilePath::FromUTF8Unsafe(*entry_path); |
| restored_watcher.recursive = recursive.value(); |
| restored_watcher.last_tag = *last_tag; |
| for (const auto& persistent_origin : *persistent_origins) { |
| if (!persistent_origin.is_string()) { |
| LOG(ERROR) << "Malformed subscriber information in preferences."; |
| continue; |
| } |
| const GURL origin_as_gurl(persistent_origin.GetString()); |
| restored_watcher.subscribers[origin_as_gurl].origin = origin_as_gurl; |
| restored_watcher.subscribers[origin_as_gurl].persistent = true; |
| } |
| restored_file_system.watchers[WatcherKey( |
| base::FilePath::FromUTF8Unsafe(*entry_path), recursive.value())] = |
| restored_watcher; |
| } |
| } |
| restored_file_systems->push_back(restored_file_system); |
| } |
| |
| return restored_file_systems; |
| } |
| |
| void Registry::UpdateWatcherTag(const ProvidedFileSystemInfo& file_system_info, |
| const Watcher& watcher) { |
| PrefService* const pref_service = profile_->GetPrefs(); |
| DCHECK(pref_service); |
| |
| // TODO(mtomasz): Consider optimizing it by moving information about watchers |
| // or even file systems to leveldb. |
| ScopedDictPrefUpdate dict_update(pref_service, |
| prefs::kFileSystemProviderMounted); |
| |
| // All of the following checks should not happen in healthy environment. |
| // However, since they rely on storage, DCHECKs can't be used. |
| base::Value::Dict* file_systems_per_extension = |
| dict_update->FindDict(file_system_info.provider_id().ToString()); |
| base::Value::Dict* file_system = nullptr; |
| base::Value::Dict* watchers = nullptr; |
| base::Value::Dict* watcher_value = nullptr; |
| |
| if (file_systems_per_extension) { |
| file_system = |
| file_systems_per_extension->FindDict(file_system_info.file_system_id()); |
| } |
| if (file_system) |
| watchers = file_system->FindDict(kPrefKeyWatchers); |
| if (watchers) |
| watcher_value = watchers->FindDict(watcher.entry_path.value()); |
| |
| if (!watcher_value) { |
| // Broken preferences. |
| LOG(ERROR) << "Broken preferences detected while updating a tag."; |
| return; |
| } |
| |
| watcher_value->Set(kPrefKeyWatcherLastTag, watcher.last_tag); |
| } |
| |
| } // namespace ash::file_system_provider |