| OLD | NEW |
| 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "chrome/browser/extensions/api/file_system/file_system_api.h" | 5 #include "chrome/browser/extensions/api/file_system/file_system_api.h" |
| 6 | 6 |
| 7 #include <set> | 7 #include <set> |
| 8 | 8 |
| 9 #include "apps/saved_files_service.h" | 9 #include "apps/saved_files_service.h" |
| 10 #include "base/bind.h" | 10 #include "base/bind.h" |
| (...skipping 40 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 51 #include "ui/base/l10n/l10n_util.h" | 51 #include "ui/base/l10n/l10n_util.h" |
| 52 #include "ui/shell_dialogs/select_file_dialog.h" | 52 #include "ui/shell_dialogs/select_file_dialog.h" |
| 53 #include "ui/shell_dialogs/selected_file_info.h" | 53 #include "ui/shell_dialogs/selected_file_info.h" |
| 54 | 54 |
| 55 #if defined(OS_MACOSX) | 55 #if defined(OS_MACOSX) |
| 56 #include <CoreFoundation/CoreFoundation.h> | 56 #include <CoreFoundation/CoreFoundation.h> |
| 57 #include "base/mac/foundation_util.h" | 57 #include "base/mac/foundation_util.h" |
| 58 #endif | 58 #endif |
| 59 | 59 |
| 60 #if defined(OS_CHROMEOS) | 60 #if defined(OS_CHROMEOS) |
| 61 #include "base/thread_task_runner_handle.h" |
| 62 #include "chrome/browser/chromeos/app_mode/kiosk_app_manager.h" |
| 61 #include "chrome/browser/chromeos/file_manager/filesystem_api_util.h" | 63 #include "chrome/browser/chromeos/file_manager/filesystem_api_util.h" |
| 64 #include "chrome/browser/chromeos/file_manager/volume_manager.h" |
| 65 #include "components/user_manager/user_manager.h" |
| 66 #include "extensions/common/manifest_handlers/kiosk_mode_info.h" |
| 62 #endif | 67 #endif |
| 63 | 68 |
| 64 using apps::SavedFileEntry; | 69 using apps::SavedFileEntry; |
| 65 using apps::SavedFilesService; | 70 using apps::SavedFilesService; |
| 66 using storage::IsolatedContext; | 71 using storage::IsolatedContext; |
| 67 | 72 |
| 68 const char kInvalidCallingPage[] = "Invalid calling page. This function can't " | 73 const char kInvalidCallingPage[] = "Invalid calling page. This function can't " |
| 69 "be called from a background page."; | 74 "be called from a background page."; |
| 70 const char kUserCancelled[] = "User cancelled"; | 75 const char kUserCancelled[] = "User cancelled"; |
| 71 const char kWritableFileErrorFormat[] = "Error opening %s"; | 76 const char kWritableFileErrorFormat[] = "Error opening %s"; |
| 72 const char kRequiresFileSystemWriteError[] = | 77 const char kRequiresFileSystemWriteError[] = |
| 73 "Operation requires fileSystem.write permission"; | 78 "Operation requires fileSystem.write permission"; |
| 74 const char kRequiresFileSystemDirectoryError[] = | 79 const char kRequiresFileSystemDirectoryError[] = |
| 75 "Operation requires fileSystem.directory permission"; | 80 "Operation requires fileSystem.directory permission"; |
| 76 const char kMultipleUnsupportedError[] = | 81 const char kMultipleUnsupportedError[] = |
| 77 "acceptsMultiple: true is not supported for 'saveFile'"; | 82 "acceptsMultiple: true is not supported for 'saveFile'"; |
| 78 const char kUnknownIdError[] = "Unknown id"; | 83 const char kUnknownIdError[] = "Unknown id"; |
| 84 const char kNotSupportedError[] = "Operation not supported."; |
| 85 |
| 86 #if defined(OS_CHROMEOS) |
| 87 const char kVolumeNotFoundError[] = "Volume not found."; |
| 88 const char kSecurityError[] = "Security error."; |
| 89 #endif |
| 79 | 90 |
| 80 namespace file_system = extensions::api::file_system; | 91 namespace file_system = extensions::api::file_system; |
| 81 namespace ChooseEntry = file_system::ChooseEntry; | 92 namespace ChooseEntry = file_system::ChooseEntry; |
| 82 | 93 |
| 83 namespace { | 94 namespace { |
| 84 | 95 |
| 85 bool g_skip_picker_for_test = false; | 96 bool g_skip_picker_for_test = false; |
| 86 bool g_use_suggested_path_for_test = false; | 97 bool g_use_suggested_path_for_test = false; |
| 87 base::FilePath* g_path_to_be_picked_for_test; | 98 base::FilePath* g_path_to_be_picked_for_test; |
| 88 std::vector<base::FilePath>* g_paths_to_be_picked_for_test; | 99 std::vector<base::FilePath>* g_paths_to_be_picked_for_test; |
| (...skipping 898 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 987 error_ = kUnknownIdError; | 998 error_ = kUnknownIdError; |
| 988 return false; | 999 return false; |
| 989 } | 1000 } |
| 990 | 1001 |
| 991 bool FileSystemGetObservedEntriesFunction::RunSync() { | 1002 bool FileSystemGetObservedEntriesFunction::RunSync() { |
| 992 NOTIMPLEMENTED(); | 1003 NOTIMPLEMENTED(); |
| 993 error_ = kUnknownIdError; | 1004 error_ = kUnknownIdError; |
| 994 return false; | 1005 return false; |
| 995 } | 1006 } |
| 996 | 1007 |
| 1008 FileSystemRequestFileSystemFunction::FileSystemRequestFileSystemFunction() |
| 1009 : chrome_details_(this) { |
| 1010 } |
| 1011 |
| 1012 ExtensionFunction::ResponseAction FileSystemRequestFileSystemFunction::Run() { |
| 1013 using extensions::api::file_system::RequestFileSystem::Params; |
| 1014 const scoped_ptr<Params> params(Params::Create(*args_)); |
| 1015 EXTENSION_FUNCTION_VALIDATE(params); |
| 1016 |
| 1017 #if !defined(OS_CHROMEOS) |
| 1018 NOTIMPLEMENTED(); |
| 1019 return RespondNow(Error(kNotSupportedError)); |
| 1020 |
| 1021 #else |
| 1022 using file_manager::VolumeManager; |
| 1023 using file_manager::VolumeInfo; |
| 1024 VolumeManager* const volume_manager = |
| 1025 VolumeManager::Get(chrome_details_.GetProfile()); |
| 1026 DCHECK(volume_manager); |
| 1027 |
| 1028 const bool writable = |
| 1029 params->options.writable.get() && *params->options.writable.get(); |
| 1030 |
| 1031 // Only kiosk apps in kiosk sessions can use this API. Additionally component |
| 1032 // extensions and apps, which is not documented though. |
| 1033 if ((!user_manager::UserManager::Get()->IsLoggedInAsKioskApp() || |
| 1034 !KioskModeInfo::IsKioskEnabled(extension())) && |
| 1035 extension()->location() != Manifest::COMPONENT) { |
| 1036 return RespondNow(Error(kNotSupportedError)); |
| 1037 } |
| 1038 |
| 1039 if (writable && |
| 1040 !app_file_handler_util::HasFileSystemWritePermission(extension_.get())) { |
| 1041 return RespondNow(Error(kRequiresFileSystemWriteError)); |
| 1042 } |
| 1043 |
| 1044 VolumeInfo volume_info; |
| 1045 if (!volume_manager->FindVolumeInfoById(params->options.volume_id, |
| 1046 &volume_info)) { |
| 1047 return RespondNow(Error(kVolumeNotFoundError)); |
| 1048 } |
| 1049 |
| 1050 const GURL site = extensions::util::GetSiteForExtensionId( |
| 1051 extension_id(), chrome_details_.GetProfile()); |
| 1052 scoped_refptr<storage::FileSystemContext> file_system_context = |
| 1053 content::BrowserContext::GetStoragePartitionForSite( |
| 1054 chrome_details_.GetProfile(), site)->GetFileSystemContext(); |
| 1055 storage::ExternalFileSystemBackend* const backend = |
| 1056 file_system_context->external_backend(); |
| 1057 DCHECK(backend); |
| 1058 |
| 1059 base::FilePath virtual_path; |
| 1060 if (!backend->GetVirtualPath(volume_info.mount_path, &virtual_path)) |
| 1061 return RespondNow(Error(kSecurityError)); |
| 1062 |
| 1063 if (writable && (volume_info.is_read_only)) |
| 1064 return RespondNow(Error(kSecurityError)); |
| 1065 |
| 1066 const bool requires_consent = |
| 1067 !chromeos::KioskAppManager::Get()->IsAutoLaunchEnabled() && |
| 1068 extension()->location() != Manifest::COMPONENT; |
| 1069 if (!requires_consent) { |
| 1070 // Grant the permission without showing the dialog. |
| 1071 base::ThreadTaskRunnerHandle::Get()->PostTask( |
| 1072 FROM_HERE, |
| 1073 base::Bind(&FileSystemRequestFileSystemFunction::OnConsentReceived, |
| 1074 this, volume_info.volume_id, writable, true /* granted */)); |
| 1075 } else { |
| 1076 // TODO(mtomasz): Create a better display name, which is the most meaningful |
| 1077 // to the user. |
| 1078 const std::string display_name = !volume_info.volume_label.empty() |
| 1079 ? volume_info.volume_label |
| 1080 : volume_info.volume_id; |
| 1081 RequestConsent( |
| 1082 display_name, writable, |
| 1083 base::Bind(&FileSystemRequestFileSystemFunction::OnConsentReceived, |
| 1084 this, volume_info.volume_id, writable)); |
| 1085 } |
| 1086 |
| 1087 return RespondLater(); |
| 1088 #endif |
| 1089 } |
| 1090 |
| 1091 #if defined(OS_CHROMEOS) |
| 1092 void FileSystemRequestFileSystemFunction::RequestConsent( |
| 1093 const std::string& display_name, |
| 1094 bool writable, |
| 1095 const base::Callback<void(bool)>& callback) { |
| 1096 // TODO(mtomasz): Implement the consent dialog. |
| 1097 callback.Run(false); |
| 1098 } |
| 1099 |
| 1100 void FileSystemRequestFileSystemFunction::OnConsentReceived( |
| 1101 const std::string& volume_id, |
| 1102 bool writable, |
| 1103 bool granted) { |
| 1104 using file_manager::VolumeManager; |
| 1105 using file_manager::VolumeInfo; |
| 1106 |
| 1107 if (!granted) { |
| 1108 SetError(kSecurityError); |
| 1109 SendResponse(false); |
| 1110 return; |
| 1111 } |
| 1112 |
| 1113 // Fetch the volume again, in case it's gone by the time the permission is |
| 1114 // granted. |
| 1115 VolumeManager* const volume_manager = |
| 1116 VolumeManager::Get(chrome_details_.GetProfile()); |
| 1117 DCHECK(volume_manager); |
| 1118 |
| 1119 VolumeInfo volume_info; |
| 1120 if (!volume_manager->FindVolumeInfoById(volume_id, &volume_info)) { |
| 1121 SetError(kVolumeNotFoundError); |
| 1122 SendResponse(false); |
| 1123 return; |
| 1124 } |
| 1125 |
| 1126 const GURL site = extensions::util::GetSiteForExtensionId( |
| 1127 extension_id(), chrome_details_.GetProfile()); |
| 1128 scoped_refptr<storage::FileSystemContext> file_system_context = |
| 1129 content::BrowserContext::GetStoragePartitionForSite( |
| 1130 chrome_details_.GetProfile(), site)->GetFileSystemContext(); |
| 1131 storage::ExternalFileSystemBackend* const backend = |
| 1132 file_system_context->external_backend(); |
| 1133 DCHECK(backend); |
| 1134 |
| 1135 // The volume may be unmounted and remounted by the time we reach this logic. |
| 1136 // TODO(mtomasz): Add a unique identifier to VolumeInfo to guarantee that the |
| 1137 // permissions are granted to exactly that volume which was plugged in when |
| 1138 // the dialog was shown. |
| 1139 base::FilePath virtual_path; |
| 1140 if (!backend->GetVirtualPath(volume_info.mount_path, &virtual_path)) { |
| 1141 SetError(kSecurityError); |
| 1142 SendResponse(false); |
| 1143 return; |
| 1144 } |
| 1145 |
| 1146 storage::IsolatedContext* const isolated_context = |
| 1147 storage::IsolatedContext::GetInstance(); |
| 1148 DCHECK(isolated_context); |
| 1149 |
| 1150 const storage::FileSystemURL original_url = |
| 1151 file_system_context->CreateCrackedFileSystemURL( |
| 1152 GURL("chrome-extension://" + extension_id()), |
| 1153 storage::kFileSystemTypeExternal, virtual_path); |
| 1154 |
| 1155 // Set a fixed register name, as the automatic one would leak the mount point |
| 1156 // directory. |
| 1157 std::string register_name = "fs"; |
| 1158 const std::string file_system_id = |
| 1159 isolated_context->RegisterFileSystemForPath( |
| 1160 storage::kFileSystemTypeNativeForPlatformApp, |
| 1161 std::string() /* file_system_id */, original_url.path(), |
| 1162 ®ister_name); |
| 1163 if (file_system_id.empty()) { |
| 1164 SetError(kSecurityError); |
| 1165 SendResponse(false); |
| 1166 return; |
| 1167 } |
| 1168 |
| 1169 backend->GrantFileAccessToExtension(extension_->id(), virtual_path); |
| 1170 |
| 1171 // Grant file permissions to the renderer hosting component. |
| 1172 content::ChildProcessSecurityPolicy* policy = |
| 1173 content::ChildProcessSecurityPolicy::GetInstance(); |
| 1174 DCHECK(policy); |
| 1175 |
| 1176 // Read-only permisisons. |
| 1177 policy->GrantReadFile(render_view_host()->GetProcess()->GetID(), |
| 1178 volume_info.mount_path); |
| 1179 policy->GrantReadFileSystem(render_view_host()->GetProcess()->GetID(), |
| 1180 file_system_id); |
| 1181 |
| 1182 // Additional write permissions. |
| 1183 if (writable) { |
| 1184 policy->GrantCreateReadWriteFile(render_view_host()->GetProcess()->GetID(), |
| 1185 volume_info.mount_path); |
| 1186 policy->GrantCopyInto(render_view_host()->GetProcess()->GetID(), |
| 1187 volume_info.mount_path); |
| 1188 policy->GrantWriteFileSystem(render_view_host()->GetProcess()->GetID(), |
| 1189 file_system_id); |
| 1190 policy->GrantDeleteFromFileSystem(render_view_host()->GetProcess()->GetID(), |
| 1191 file_system_id); |
| 1192 policy->GrantCreateFileForFileSystem( |
| 1193 render_view_host()->GetProcess()->GetID(), file_system_id); |
| 1194 } |
| 1195 |
| 1196 base::DictionaryValue* const dict = new base::DictionaryValue(); |
| 1197 dict->SetString("file_system_id", file_system_id); |
| 1198 dict->SetString("file_system_path", register_name); |
| 1199 |
| 1200 SetResult(dict); |
| 1201 SendResponse(true); |
| 1202 } |
| 1203 #endif |
| 1204 |
| 997 } // namespace extensions | 1205 } // namespace extensions |
| OLD | NEW |