Chromium Code Reviews| Index: chrome/browser/extensions/api/file_system/file_system_api.cc |
| diff --git a/chrome/browser/extensions/api/file_system/file_system_api.cc b/chrome/browser/extensions/api/file_system/file_system_api.cc |
| index dbe8bfdc83aa506e9c2dc6cabb020662319e8094..723060f44088ae64c50ebcca6f87498563419cd0 100644 |
| --- a/chrome/browser/extensions/api/file_system/file_system_api.cc |
| +++ b/chrome/browser/extensions/api/file_system/file_system_api.cc |
| @@ -58,7 +58,11 @@ |
| #endif |
| #if defined(OS_CHROMEOS) |
| +#include "chrome/browser/chromeos/app_mode/kiosk_app_manager.h" |
| #include "chrome/browser/chromeos/file_manager/filesystem_api_util.h" |
| +#include "chrome/browser/chromeos/file_manager/volume_manager.h" |
| +#include "components/user_manager/user_manager.h" |
| +#include "extensions/common/manifest_handlers/kiosk_mode_info.h" |
| #endif |
| using apps::SavedFileEntry; |
| @@ -76,6 +80,9 @@ const char kRequiresFileSystemDirectoryError[] = |
| const char kMultipleUnsupportedError[] = |
| "acceptsMultiple: true is not supported for 'saveFile'"; |
| const char kUnknownIdError[] = "Unknown id"; |
| +const char kVolumeNotFoundError[] = "Volume not found."; |
| +const char kSecurityError[] = "Security error."; |
| +const char kNotSupportedError[] = "Operation not supported."; |
| namespace file_system = extensions::api::file_system; |
| namespace ChooseEntry = file_system::ChooseEntry; |
| @@ -994,4 +1001,196 @@ bool FileSystemGetObservedEntriesFunction::RunSync() { |
| return false; |
| } |
| +FileSystemRequestFileSystemFunction::FileSystemRequestFileSystemFunction() |
| + : chrome_details_(this) { |
| +} |
| + |
| +ExtensionFunction::ResponseAction FileSystemRequestFileSystemFunction::Run() { |
| + using extensions::api::file_system::RequestFileSystem::Params; |
| + const scoped_ptr<Params> params(Params::Create(*args_)); |
| + EXTENSION_FUNCTION_VALIDATE(params); |
| + |
| +#if !defined(OS_CHROMEOS) |
| + NOTIMPLEMENTED(); |
| + return RespondNow(Error(kNotSupportedError)); |
| + |
| +#else |
| + using file_manager::VolumeManager; |
| + using file_manager::VolumeInfo; |
| + VolumeManager* const volume_manager = |
| + VolumeManager::Get(chrome_details_.GetProfile()); |
| + DCHECK(volume_manager); |
| + |
| + const bool writable = |
| + params->options.writable.get() && *params->options.writable.get(); |
| + LOG(ERROR) << params->options.volume_id << ": " << writable; |
|
hirono
2015/03/18 03:41:05
nit: Debug log.
mtomasz
2015/03/19 01:17:20
Done.
|
| + |
| + // Only kiosk apps in kiosk sessions can use this API. Additionally component |
| + // extensions and apps, which is not documented though. |
| + if ((!user_manager::UserManager::Get()->IsLoggedInAsKioskApp() || |
| + !KioskModeInfo::IsKioskEnabled(extension())) && |
| + extension()->location() != Manifest::COMPONENT) { |
| + return RespondNow(Error(kNotSupportedError)); |
| + } |
| + |
| + if (writable && |
| + !app_file_handler_util::HasFileSystemWritePermission(extension_.get())) { |
| + return RespondNow(Error(kRequiresFileSystemWriteError)); |
| + } |
| + |
| + VolumeInfo volume_info; |
| + if (!volume_manager->FindVolumeInfoById(params->options.volume_id, |
| + &volume_info)) { |
| + return RespondNow(Error(kVolumeNotFoundError)); |
| + } |
| + |
| + const GURL site = extensions::util::GetSiteForExtensionId( |
| + extension_id(), chrome_details_.GetProfile()); |
| + scoped_refptr<storage::FileSystemContext> file_system_context = |
| + content::BrowserContext::GetStoragePartitionForSite( |
| + chrome_details_.GetProfile(), site)->GetFileSystemContext(); |
| + storage::ExternalFileSystemBackend* const backend = |
| + file_system_context->external_backend(); |
| + DCHECK(backend); |
| + |
| + base::FilePath virtual_path; |
| + if (!backend->GetVirtualPath(volume_info.mount_path, &virtual_path)) |
| + return RespondNow(Error(kSecurityError)); |
| + |
| + if (writable && (volume_info.is_read_only)) |
| + return RespondNow(Error(kSecurityError)); |
| + |
| + const bool requires_consent = |
| + !chromeos::KioskAppManager::Get()->IsAutoLaunchEnabled() && |
| + extension()->location() != Manifest::COMPONENT; |
| + if (!requires_consent) { |
| + OnConsentReceived(volume_info.volume_id, writable, |
|
hirono
2015/03/18 03:41:05
Just confirmation, if OnConsentRecived called dire
mtomasz
2015/03/19 01:17:20
Good catch. It works as RespondLater() simply retu
|
| + true); // Grant without user consent. |
| + } else { |
| + // TODO(mtomasz): Create a better display name, which is the most meaningful |
| + // to the user. |
| + const std::string display_name = !volume_info.volume_label.empty() |
| + ? volume_info.volume_label |
| + : volume_info.volume_id; |
| + RequestConsent( |
| + display_name, writable, |
| + base::Bind(&FileSystemRequestFileSystemFunction::OnConsentReceived, |
| + this, volume_info.volume_id, writable)); |
| + } |
| + |
| + return RespondLater(); |
| +#endif |
| +} |
| + |
| +void FileSystemRequestFileSystemFunction::RequestConsent( |
| + const std::string& display_name, |
| + bool writable, |
| + const base::Callback<void(bool)>& callback) { |
| + // TODO(mtomasz): Implement the consent dialog. |
| + callback.Run(false); |
| +} |
| + |
| +void FileSystemRequestFileSystemFunction::OnConsentReceived( |
| + const std::string& volume_id, |
| + bool writable, |
| + bool granted) { |
| + if (!granted) { |
| + SetError(kSecurityError); |
| + return; |
| + } |
| + |
| +#if defined(OS_CHROMEOS) |
|
hirono
2015/03/18 03:41:05
How about enclosing entire RequsetContext and OnCo
mtomasz
2015/03/19 01:17:20
Done.
|
| + using file_manager::VolumeManager; |
| + using file_manager::VolumeInfo; |
| + |
| + // Fetch the volume again, in case it's gone by the time the permission is |
| + // granted. |
| + VolumeManager* const volume_manager = |
| + VolumeManager::Get(chrome_details_.GetProfile()); |
| + DCHECK(volume_manager); |
| + |
| + VolumeInfo volume_info; |
| + if (!volume_manager->FindVolumeInfoById(volume_id, &volume_info)) { |
| + SetError(kVolumeNotFoundError); |
| + SendResponse(false); |
| + return; |
| + } |
| + |
| + const GURL site = extensions::util::GetSiteForExtensionId( |
| + extension_id(), chrome_details_.GetProfile()); |
| + scoped_refptr<storage::FileSystemContext> file_system_context = |
| + content::BrowserContext::GetStoragePartitionForSite( |
| + chrome_details_.GetProfile(), site)->GetFileSystemContext(); |
| + storage::ExternalFileSystemBackend* const backend = |
| + file_system_context->external_backend(); |
| + DCHECK(backend); |
| + |
| + // The volume may be unmounted and remounted by the time we reach this logic. |
| + // TODO(mtomasz): Add a unique identifier to VolumeInfo to guarantee that the |
| + // permissions are granted to exactly that volume which was plugged in when |
| + // the dialog was shown. |
| + base::FilePath virtual_path; |
| + if (!backend->GetVirtualPath(volume_info.mount_path, &virtual_path)) { |
| + SetError(kSecurityError); |
| + SendResponse(false); |
| + return; |
| + } |
| + |
| + storage::IsolatedContext* const isolated_context = |
| + storage::IsolatedContext::GetInstance(); |
| + DCHECK(isolated_context); |
| + |
| + const storage::FileSystemURL original_url = |
| + file_system_context->CreateCrackedFileSystemURL( |
| + GURL("chrome-extension://" + extension_id()), |
| + storage::kFileSystemTypeExternal, virtual_path); |
| + |
| + std::string register_name = "fs"; |
|
hirono
2015/03/18 03:41:05
Do we need to assign "fs"?
mtomasz
2015/03/19 01:17:20
Without it we would leak the mount point path, we
|
| + const std::string file_system_id = |
| + isolated_context->RegisterFileSystemForPath( |
| + storage::kFileSystemTypeNativeForPlatformApp, |
| + std::string() /* file_system_id */, original_url.path(), |
| + ®ister_name); |
| + if (file_system_id.empty()) { |
| + SetError(kSecurityError); |
| + SendResponse(false); |
| + return; |
| + } |
| + |
| + backend->GrantFileAccessToExtension(extension_->id(), virtual_path); |
| + |
| + // Grant file permissions to the renderer hosting component. |
| + content::ChildProcessSecurityPolicy* policy = |
| + content::ChildProcessSecurityPolicy::GetInstance(); |
| + DCHECK(policy); |
| + |
| + // Read-only permisisons. |
| + policy->GrantReadFile(render_view_host()->GetProcess()->GetID(), |
| + volume_info.mount_path); |
| + policy->GrantReadFileSystem(render_view_host()->GetProcess()->GetID(), |
| + file_system_id); |
| + |
| + // Additional write permissions. |
| + if (writable) { |
| + policy->GrantCreateReadWriteFile(render_view_host()->GetProcess()->GetID(), |
| + volume_info.mount_path); |
| + policy->GrantCopyInto(render_view_host()->GetProcess()->GetID(), |
| + volume_info.mount_path); |
| + policy->GrantWriteFileSystem(render_view_host()->GetProcess()->GetID(), |
| + file_system_id); |
| + policy->GrantDeleteFromFileSystem(render_view_host()->GetProcess()->GetID(), |
| + file_system_id); |
| + policy->GrantCreateFileForFileSystem( |
| + render_view_host()->GetProcess()->GetID(), file_system_id); |
| + } |
| + |
| + base::DictionaryValue* const dict = new base::DictionaryValue(); |
| + dict->SetString("file_system_id", file_system_id); |
| + dict->SetString("file_system_path", register_name); |
| + |
| + SetResult(dict); |
| + SendResponse(true); |
| +#endif |
| +} |
| + |
| } // namespace extensions |