Chromium Code Reviews| 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."; | |
|
benwells
2015/03/20 00:42:39
It would be good to provide users with info on why
mtomasz
2015/03/20 01:56:00
Done.
| |
| 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 | |
|
benwells
2015/03/20 00:42:39
This check (kiosk mode) can go up before the code
mtomasz
2015/03/20 01:56:00
Done.
| |
| 1032 // extensions and apps, which is not documented though. | |
| 1033 if ((!user_manager::UserManager::Get()->IsLoggedInAsKioskApp() || | |
| 1034 !KioskModeInfo::IsKioskEnabled(extension())) && | |
|
benwells
2015/03/20 00:42:39
I don't understand kiosk mode that well but rememb
mtomasz
2015/03/20 01:56:00
Yes, the API is meant to work in both modes, but f
| |
| 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; | |
|
benwells
2015/03/20 00:42:39
Right now this will never be true due to the check
mtomasz
2015/03/20 01:56:00
Not really. It will be true for kiosk apps launche
benwells
2015/03/20 02:43:57
Ohhh, I'd misread the logic above...
| |
| 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); | |
|
benwells
2015/03/20 00:42:39
I think you should use PostTask here, as you're ca
mtomasz
2015/03/20 01:56:00
Done.
| |
| 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); | |
|
benwells
2015/03/20 00:42:39
Is this the correct error?
mtomasz
2015/03/20 01:56:00
GetVirtualPath() is not used to check if the volum
| |
| 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()), | |
|
benwells
2015/03/20 00:42:39
I think there is a constant you can use for the sc
mtomasz
2015/03/20 01:56:00
Done.
| |
| 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 |