Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(35)

Unified Diff: chrome/browser/extensions/api/file_system/file_system_api.cc

Issue 985533004: Implement chrome.fileSystem.requestFileSystem(). (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Fixed. Created 5 years, 9 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
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.";
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.
+
+#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
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.
+ // extensions and apps, which is not documented though.
+ if ((!user_manager::UserManager::Get()->IsLoggedInAsKioskApp() ||
+ !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
+ 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;
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...
+ 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);
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.
+}
+
+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);
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
+ 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()),
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.
+ 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(),
+ &register_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

Powered by Google App Engine
This is Rietveld 408576698