| 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
|
|
|