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