| 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..972f0f31eb9c69c713ddd84641e5b3264c0b7a53 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,12 @@ | 
| #endif | 
|  | 
| #if defined(OS_CHROMEOS) | 
| +#include "base/thread_task_runner_handle.h" | 
| +#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 +81,12 @@ const char kRequiresFileSystemDirectoryError[] = | 
| const char kMultipleUnsupportedError[] = | 
| "acceptsMultiple: true is not supported for 'saveFile'"; | 
| const char kUnknownIdError[] = "Unknown id"; | 
| +const char kNotSupportedError[] = "Operation not supported."; | 
| + | 
| +#if defined(OS_CHROMEOS) | 
| +const char kVolumeNotFoundError[] = "Volume not found."; | 
| +const char kSecurityError[] = "Security error."; | 
| +#endif | 
|  | 
| namespace file_system = extensions::api::file_system; | 
| namespace ChooseEntry = file_system::ChooseEntry; | 
| @@ -994,4 +1005,201 @@ 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(); | 
| + | 
| +  // 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) { | 
| +    // Grant the permission without showing the dialog. | 
| +    base::ThreadTaskRunnerHandle::Get()->PostTask( | 
| +        FROM_HERE, | 
| +        base::Bind(&FileSystemRequestFileSystemFunction::OnConsentReceived, | 
| +                   this, volume_info.volume_id, writable, true /* granted */)); | 
| +  } 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 | 
| +} | 
| + | 
| +#if defined(OS_CHROMEOS) | 
| +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) { | 
| +  using file_manager::VolumeManager; | 
| +  using file_manager::VolumeInfo; | 
| + | 
| +  if (!granted) { | 
| +    SetError(kSecurityError); | 
| +    SendResponse(false); | 
| +    return; | 
| +  } | 
| + | 
| +  // 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); | 
| + | 
| +  // Set a fixed register name, as the automatic one would leak the mount point | 
| +  // directory. | 
| +  std::string register_name = "fs"; | 
| +  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 | 
|  |