blob: 99439b742ac06bb8d7cef00da0376a8fb317f6cb [file] [log] [blame]
// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "content/browser/fileapi/fileapi_message_filter.h"
#include <string>
#include <vector>
#include "base/bind.h"
#include "base/files/file_path.h"
#include "base/logging.h"
#include "base/memory/scoped_ptr.h"
#include "base/platform_file.h"
#include "base/sequenced_task_runner.h"
#include "base/strings/string_util.h"
#include "base/threading/thread.h"
#include "base/time/time.h"
#include "content/browser/child_process_security_policy_impl.h"
#include "content/browser/fileapi/browser_file_system_helper.h"
#include "content/browser/fileapi/chrome_blob_storage_context.h"
#include "content/browser/streams/stream_registry.h"
#include "content/common/fileapi/file_system_messages.h"
#include "content/common/fileapi/webblob_messages.h"
#include "content/public/browser/user_metrics.h"
#include "ipc/ipc_platform_file.h"
#include "net/base/mime_util.h"
#include "net/url_request/url_request_context.h"
#include "net/url_request/url_request_context_getter.h"
#include "url/gurl.h"
#include "webkit/browser/blob/blob_storage_context.h"
#include "webkit/browser/blob/blob_storage_host.h"
#include "webkit/browser/fileapi/file_observers.h"
#include "webkit/browser/fileapi/file_permission_policy.h"
#include "webkit/browser/fileapi/file_system_context.h"
#include "webkit/browser/fileapi/isolated_context.h"
#include "webkit/browser/quota/quota_manager.h"
#include "webkit/common/blob/blob_data.h"
#include "webkit/common/blob/shareable_file_reference.h"
#include "webkit/common/fileapi/directory_entry.h"
#include "webkit/common/fileapi/file_system_info.h"
#include "webkit/common/fileapi/file_system_types.h"
#include "webkit/common/fileapi/file_system_util.h"
#if defined(ENABLE_PLUGINS)
#include "content/browser/renderer_host/pepper/pepper_security_helper.h"
#include "ppapi/shared_impl/file_type_conversion.h"
#endif // defined(ENABLE_PLUGINS)
using fileapi::FileSystemFileUtil;
using fileapi::FileSystemBackend;
using fileapi::FileSystemOperation;
using fileapi::FileSystemURL;
using fileapi::FileUpdateObserver;
using fileapi::UpdateObserverList;
using webkit_blob::BlobData;
using webkit_blob::BlobStorageContext;
using webkit_blob::BlobStorageHost;
namespace content {
namespace {
void RevokeFilePermission(int child_id, const base::FilePath& path) {
ChildProcessSecurityPolicyImpl::GetInstance()->RevokeAllPermissionsForFile(
child_id, path);
}
} // namespace
FileAPIMessageFilter::FileAPIMessageFilter(
int process_id,
net::URLRequestContextGetter* request_context_getter,
fileapi::FileSystemContext* file_system_context,
ChromeBlobStorageContext* blob_storage_context,
StreamContext* stream_context)
: process_id_(process_id),
context_(file_system_context),
security_policy_(ChildProcessSecurityPolicyImpl::GetInstance()),
request_context_getter_(request_context_getter),
request_context_(NULL),
blob_storage_context_(blob_storage_context),
stream_context_(stream_context) {
DCHECK(context_);
DCHECK(request_context_getter_.get());
DCHECK(blob_storage_context);
DCHECK(stream_context);
}
FileAPIMessageFilter::FileAPIMessageFilter(
int process_id,
net::URLRequestContext* request_context,
fileapi::FileSystemContext* file_system_context,
ChromeBlobStorageContext* blob_storage_context,
StreamContext* stream_context)
: process_id_(process_id),
context_(file_system_context),
security_policy_(ChildProcessSecurityPolicyImpl::GetInstance()),
request_context_(request_context),
blob_storage_context_(blob_storage_context),
stream_context_(stream_context) {
DCHECK(context_);
DCHECK(request_context_);
DCHECK(blob_storage_context);
DCHECK(stream_context);
}
void FileAPIMessageFilter::OnChannelConnected(int32 peer_pid) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
if (request_context_getter_.get()) {
DCHECK(!request_context_);
request_context_ = request_context_getter_->GetURLRequestContext();
request_context_getter_ = NULL;
DCHECK(request_context_);
}
blob_storage_host_.reset(
new BlobStorageHost(blob_storage_context_->context()));
operation_runner_ = context_->CreateFileSystemOperationRunner();
}
void FileAPIMessageFilter::OnChannelClosing() {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
// Unregister all the blob and stream URLs that are previously registered in
// this process.
blob_storage_host_.reset();
for (base::hash_set<std::string>::const_iterator iter = stream_urls_.begin();
iter != stream_urls_.end(); ++iter) {
stream_context_->registry()->UnregisterStream(GURL(*iter));
}
in_transit_snapshot_files_.clear();
// Close all files that are previously OpenFile()'ed in this process.
if (!on_close_callbacks_.IsEmpty()) {
DLOG(INFO)
<< "File API: Renderer process shut down before NotifyCloseFile"
<< " for " << on_close_callbacks_.size() << " files opened in PPAPI";
}
for (OnCloseCallbackMap::iterator itr(&on_close_callbacks_);
!itr.IsAtEnd(); itr.Advance()) {
const base::Closure* callback = itr.GetCurrentValue();
DCHECK(callback);
if (!callback->is_null())
callback->Run();
}
on_close_callbacks_.Clear();
operation_runner_.reset();
operations_.clear();
}
base::TaskRunner* FileAPIMessageFilter::OverrideTaskRunnerForMessage(
const IPC::Message& message) {
if (message.type() == FileSystemHostMsg_SyncGetPlatformPath::ID)
return context_->default_file_task_runner();
return NULL;
}
bool FileAPIMessageFilter::OnMessageReceived(
const IPC::Message& message, bool* message_was_ok) {
*message_was_ok = true;
bool handled = true;
IPC_BEGIN_MESSAGE_MAP_EX(FileAPIMessageFilter, message, *message_was_ok)
IPC_MESSAGE_HANDLER(FileSystemHostMsg_OpenFileSystem, OnOpenFileSystem)
IPC_MESSAGE_HANDLER(FileSystemHostMsg_ResolveURL, OnResolveURL)
IPC_MESSAGE_HANDLER(FileSystemHostMsg_DeleteFileSystem, OnDeleteFileSystem)
IPC_MESSAGE_HANDLER(FileSystemHostMsg_Move, OnMove)
IPC_MESSAGE_HANDLER(FileSystemHostMsg_Copy, OnCopy)
IPC_MESSAGE_HANDLER(FileSystemHostMsg_Remove, OnRemove)
IPC_MESSAGE_HANDLER(FileSystemHostMsg_ReadMetadata, OnReadMetadata)
IPC_MESSAGE_HANDLER(FileSystemHostMsg_Create, OnCreate)
IPC_MESSAGE_HANDLER(FileSystemHostMsg_Exists, OnExists)
IPC_MESSAGE_HANDLER(FileSystemHostMsg_ReadDirectory, OnReadDirectory)
IPC_MESSAGE_HANDLER(FileSystemHostMsg_Write, OnWrite)
IPC_MESSAGE_HANDLER(FileSystemHostMsg_Truncate, OnTruncate)
IPC_MESSAGE_HANDLER(FileSystemHostMsg_TouchFile, OnTouchFile)
IPC_MESSAGE_HANDLER(FileSystemHostMsg_CancelWrite, OnCancel)
#if defined(ENABLE_PLUGINS)
IPC_MESSAGE_HANDLER(FileSystemHostMsg_OpenPepperFile, OnOpenPepperFile)
#endif // defined(ENABLE_PLUGINS)
IPC_MESSAGE_HANDLER(FileSystemHostMsg_NotifyCloseFile, OnNotifyCloseFile)
IPC_MESSAGE_HANDLER(FileSystemHostMsg_CreateSnapshotFile,
OnCreateSnapshotFile)
IPC_MESSAGE_HANDLER(FileSystemHostMsg_DidReceiveSnapshotFile,
OnDidReceiveSnapshotFile)
IPC_MESSAGE_HANDLER(FileSystemHostMsg_WillUpdate, OnWillUpdate)
IPC_MESSAGE_HANDLER(FileSystemHostMsg_DidUpdate, OnDidUpdate)
IPC_MESSAGE_HANDLER(FileSystemHostMsg_SyncGetPlatformPath,
OnSyncGetPlatformPath)
IPC_MESSAGE_HANDLER(BlobHostMsg_StartBuilding, OnStartBuildingBlob)
IPC_MESSAGE_HANDLER(BlobHostMsg_AppendBlobDataItem,
OnAppendBlobDataItemToBlob)
IPC_MESSAGE_HANDLER(BlobHostMsg_SyncAppendSharedMemory,
OnAppendSharedMemoryToBlob)
IPC_MESSAGE_HANDLER(BlobHostMsg_FinishBuilding, OnFinishBuildingBlob)
IPC_MESSAGE_HANDLER(BlobHostMsg_IncrementRefCount,
OnIncrementBlobRefCount)
IPC_MESSAGE_HANDLER(BlobHostMsg_DecrementRefCount,
OnDecrementBlobRefCount)
IPC_MESSAGE_HANDLER(BlobHostMsg_RegisterPublicURL,
OnRegisterPublicBlobURL)
IPC_MESSAGE_HANDLER(BlobHostMsg_RevokePublicURL, OnRevokePublicBlobURL)
IPC_MESSAGE_HANDLER(StreamHostMsg_StartBuilding, OnStartBuildingStream)
IPC_MESSAGE_HANDLER(StreamHostMsg_AppendBlobDataItem,
OnAppendBlobDataItemToStream)
IPC_MESSAGE_HANDLER(StreamHostMsg_SyncAppendSharedMemory,
OnAppendSharedMemoryToStream)
IPC_MESSAGE_HANDLER(StreamHostMsg_FinishBuilding, OnFinishBuildingStream)
IPC_MESSAGE_HANDLER(StreamHostMsg_AbortBuilding, OnAbortBuildingStream)
IPC_MESSAGE_HANDLER(StreamHostMsg_Clone, OnCloneStream)
IPC_MESSAGE_HANDLER(StreamHostMsg_Remove, OnRemoveStream)
IPC_MESSAGE_UNHANDLED(handled = false)
IPC_END_MESSAGE_MAP_EX()
return handled;
}
FileAPIMessageFilter::~FileAPIMessageFilter() {}
void FileAPIMessageFilter::BadMessageReceived() {
RecordAction(UserMetricsAction("BadMessageTerminate_FAMF"));
BrowserMessageFilter::BadMessageReceived();
}
void FileAPIMessageFilter::OnOpenFileSystem(int request_id,
const GURL& origin_url,
fileapi::FileSystemType type) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
if (type == fileapi::kFileSystemTypeTemporary) {
RecordAction(UserMetricsAction("OpenFileSystemTemporary"));
} else if (type == fileapi::kFileSystemTypePersistent) {
RecordAction(UserMetricsAction("OpenFileSystemPersistent"));
}
fileapi::OpenFileSystemMode mode =
fileapi::OPEN_FILE_SYSTEM_CREATE_IF_NONEXISTENT;
context_->OpenFileSystem(origin_url, type, mode, base::Bind(
&FileAPIMessageFilter::DidOpenFileSystem, this, request_id));
}
void FileAPIMessageFilter::OnResolveURL(
int request_id,
const GURL& filesystem_url) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
FileSystemURL url(context_->CrackURL(filesystem_url));
if (!ValidateFileSystemURL(request_id, url))
return;
if (!security_policy_->CanReadFileSystemFile(process_id_, url)) {
Send(new FileSystemMsg_DidFail(request_id,
base::PLATFORM_FILE_ERROR_SECURITY));
return;
}
context_->ResolveURL(url, base::Bind(
&FileAPIMessageFilter::DidResolveURL, this, request_id));
}
void FileAPIMessageFilter::OnDeleteFileSystem(
int request_id,
const GURL& origin_url,
fileapi::FileSystemType type) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
context_->DeleteFileSystem(origin_url, type, base::Bind(
&FileAPIMessageFilter::DidDeleteFileSystem, this, request_id));
}
void FileAPIMessageFilter::OnMove(
int request_id, const GURL& src_path, const GURL& dest_path) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
FileSystemURL src_url(context_->CrackURL(src_path));
FileSystemURL dest_url(context_->CrackURL(dest_path));
if (!ValidateFileSystemURL(request_id, src_url) ||
!ValidateFileSystemURL(request_id, dest_url)) {
return;
}
if (!security_policy_->CanReadFileSystemFile(process_id_, src_url) ||
!security_policy_->CanDeleteFileSystemFile(process_id_, src_url) ||
!security_policy_->CanCreateFileSystemFile(process_id_, dest_url)) {
Send(new FileSystemMsg_DidFail(request_id,
base::PLATFORM_FILE_ERROR_SECURITY));
return;
}
operations_[request_id] = operation_runner()->Move(
src_url, dest_url,
fileapi::FileSystemOperation::OPTION_NONE,
base::Bind(&FileAPIMessageFilter::DidFinish, this, request_id));
}
void FileAPIMessageFilter::OnCopy(
int request_id, const GURL& src_path, const GURL& dest_path) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
FileSystemURL src_url(context_->CrackURL(src_path));
FileSystemURL dest_url(context_->CrackURL(dest_path));
if (!ValidateFileSystemURL(request_id, src_url) ||
!ValidateFileSystemURL(request_id, dest_url)) {
return;
}
if (!security_policy_->CanReadFileSystemFile(process_id_, src_url) ||
!security_policy_->CanCopyIntoFileSystemFile(process_id_, dest_url)) {
Send(new FileSystemMsg_DidFail(request_id,
base::PLATFORM_FILE_ERROR_SECURITY));
return;
}
operations_[request_id] = operation_runner()->Copy(
src_url, dest_url,
fileapi::FileSystemOperation::OPTION_NONE,
fileapi::FileSystemOperationRunner::CopyProgressCallback(),
base::Bind(&FileAPIMessageFilter::DidFinish, this, request_id));
}
void FileAPIMessageFilter::OnRemove(
int request_id, const GURL& path, bool recursive) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
FileSystemURL url(context_->CrackURL(path));
if (!ValidateFileSystemURL(request_id, url))
return;
if (!security_policy_->CanDeleteFileSystemFile(process_id_, url)) {
Send(new FileSystemMsg_DidFail(request_id,
base::PLATFORM_FILE_ERROR_SECURITY));
return;
}
operations_[request_id] = operation_runner()->Remove(
url, recursive,
base::Bind(&FileAPIMessageFilter::DidFinish, this, request_id));
}
void FileAPIMessageFilter::OnReadMetadata(
int request_id, const GURL& path) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
FileSystemURL url(context_->CrackURL(path));
if (!ValidateFileSystemURL(request_id, url))
return;
if (!security_policy_->CanReadFileSystemFile(process_id_, url)) {
Send(new FileSystemMsg_DidFail(request_id,
base::PLATFORM_FILE_ERROR_SECURITY));
return;
}
operations_[request_id] = operation_runner()->GetMetadata(
url, base::Bind(&FileAPIMessageFilter::DidGetMetadata, this, request_id));
}
void FileAPIMessageFilter::OnCreate(
int request_id, const GURL& path, bool exclusive,
bool is_directory, bool recursive) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
FileSystemURL url(context_->CrackURL(path));
if (!ValidateFileSystemURL(request_id, url))
return;
if (!security_policy_->CanCreateFileSystemFile(process_id_, url)) {
Send(new FileSystemMsg_DidFail(request_id,
base::PLATFORM_FILE_ERROR_SECURITY));
return;
}
if (is_directory) {
operations_[request_id] = operation_runner()->CreateDirectory(
url, exclusive, recursive,
base::Bind(&FileAPIMessageFilter::DidFinish, this, request_id));
} else {
operations_[request_id] = operation_runner()->CreateFile(
url, exclusive,
base::Bind(&FileAPIMessageFilter::DidFinish, this, request_id));
}
}
void FileAPIMessageFilter::OnExists(
int request_id, const GURL& path, bool is_directory) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
FileSystemURL url(context_->CrackURL(path));
if (!ValidateFileSystemURL(request_id, url))
return;
if (!security_policy_->CanReadFileSystemFile(process_id_, url)) {
Send(new FileSystemMsg_DidFail(request_id,
base::PLATFORM_FILE_ERROR_SECURITY));
return;
}
if (is_directory) {
operations_[request_id] = operation_runner()->DirectoryExists(
url,
base::Bind(&FileAPIMessageFilter::DidFinish, this, request_id));
} else {
operations_[request_id] = operation_runner()->FileExists(
url,
base::Bind(&FileAPIMessageFilter::DidFinish, this, request_id));
}
}
void FileAPIMessageFilter::OnReadDirectory(
int request_id, const GURL& path) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
FileSystemURL url(context_->CrackURL(path));
if (!ValidateFileSystemURL(request_id, url))
return;
if (!security_policy_->CanReadFileSystemFile(process_id_, url)) {
Send(new FileSystemMsg_DidFail(request_id,
base::PLATFORM_FILE_ERROR_SECURITY));
return;
}
operations_[request_id] = operation_runner()->ReadDirectory(
url, base::Bind(&FileAPIMessageFilter::DidReadDirectory,
this, request_id));
}
void FileAPIMessageFilter::OnWrite(
int request_id,
const GURL& path,
const std::string& blob_uuid,
int64 offset) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
if (!request_context_) {
// We can't write w/o a request context, trying to do so will crash.
NOTREACHED();
return;
}
FileSystemURL url(context_->CrackURL(path));
if (!ValidateFileSystemURL(request_id, url))
return;
if (!security_policy_->CanWriteFileSystemFile(process_id_, url)) {
Send(new FileSystemMsg_DidFail(request_id,
base::PLATFORM_FILE_ERROR_SECURITY));
return;
}
scoped_ptr<webkit_blob::BlobDataHandle> blob =
blob_storage_context_->context()->GetBlobDataFromUUID(blob_uuid);
operations_[request_id] = operation_runner()->Write(
request_context_, url, blob.Pass(), offset,
base::Bind(&FileAPIMessageFilter::DidWrite, this, request_id));
}
void FileAPIMessageFilter::OnTruncate(
int request_id,
const GURL& path,
int64 length) {
FileSystemURL url(context_->CrackURL(path));
if (!ValidateFileSystemURL(request_id, url))
return;
if (!security_policy_->CanWriteFileSystemFile(process_id_, url)) {
Send(new FileSystemMsg_DidFail(request_id,
base::PLATFORM_FILE_ERROR_SECURITY));
return;
}
operations_[request_id] = operation_runner()->Truncate(
url, length,
base::Bind(&FileAPIMessageFilter::DidFinish, this, request_id));
}
void FileAPIMessageFilter::OnTouchFile(
int request_id,
const GURL& path,
const base::Time& last_access_time,
const base::Time& last_modified_time) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
FileSystemURL url(context_->CrackURL(path));
if (!ValidateFileSystemURL(request_id, url))
return;
if (!security_policy_->CanCreateFileSystemFile(process_id_, url)) {
Send(new FileSystemMsg_DidFail(request_id,
base::PLATFORM_FILE_ERROR_SECURITY));
return;
}
operations_[request_id] = operation_runner()->TouchFile(
url, last_access_time, last_modified_time,
base::Bind(&FileAPIMessageFilter::DidFinish, this, request_id));
}
void FileAPIMessageFilter::OnCancel(
int request_id,
int request_id_to_cancel) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
OperationsMap::iterator found = operations_.find(request_id_to_cancel);
if (found != operations_.end()) {
// The cancel will eventually send both the write failure and the cancel
// success.
operation_runner()->Cancel(
found->second,
base::Bind(&FileAPIMessageFilter::DidFinish, this, request_id));
} else {
// The write already finished; report that we failed to stop it.
Send(new FileSystemMsg_DidFail(
request_id, base::PLATFORM_FILE_ERROR_INVALID_OPERATION));
}
}
#if defined(ENABLE_PLUGINS)
void FileAPIMessageFilter::OnOpenPepperFile(
int request_id, const GURL& path, int pp_open_flags) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
FileSystemURL url(context_->CrackURL(path));
if (!ValidateFileSystemURL(request_id, url))
return;
if (!CanOpenFileSystemURLWithPepperFlags(pp_open_flags, process_id_, url)) {
Send(new FileSystemMsg_DidFail(
request_id, base::PLATFORM_FILE_ERROR_SECURITY));
return;
}
quota::QuotaLimitType quota_policy = quota::kQuotaLimitTypeUnknown;
quota::QuotaManagerProxy* quota_manager_proxy =
context_->quota_manager_proxy();
CHECK(quota_manager_proxy);
CHECK(quota_manager_proxy->quota_manager());
if (quota_manager_proxy->quota_manager()->IsStorageUnlimited(
url.origin(), FileSystemTypeToQuotaStorageType(url.type()))) {
quota_policy = quota::kQuotaLimitTypeUnlimited;
} else {
quota_policy = quota::kQuotaLimitTypeLimited;
}
int platform_file_flags = 0;
if (!ppapi::PepperFileOpenFlagsToPlatformFileFlags(pp_open_flags,
&platform_file_flags)) {
// |pp_open_flags| should have already been checked in PepperFileIOHost.
NOTREACHED() << "Open file request with invalid pp_open_flags ignored.";
}
operations_[request_id] = operation_runner()->OpenFile(
url, platform_file_flags, PeerHandle(),
base::Bind(&FileAPIMessageFilter::DidOpenFile, this, request_id,
quota_policy));
}
#endif // defined(ENABLE_PLUGINS)
void FileAPIMessageFilter::OnNotifyCloseFile(int file_open_id) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
// Remove |file_open_id| from the map of |on_close_callback|s.
// It must only be called for a ID that is successfully opened and enrolled in
// DidOpenFile.
base::Closure* on_close_callback = on_close_callbacks_.Lookup(file_open_id);
if (on_close_callback && !on_close_callback->is_null()) {
on_close_callback->Run();
on_close_callbacks_.Remove(file_open_id);
}
}
void FileAPIMessageFilter::OnWillUpdate(const GURL& path) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
FileSystemURL url(context_->CrackURL(path));
if (!url.is_valid())
return;
const UpdateObserverList* observers =
context_->GetUpdateObservers(url.type());
if (!observers)
return;
observers->Notify(&FileUpdateObserver::OnStartUpdate, MakeTuple(url));
}
void FileAPIMessageFilter::OnDidUpdate(const GURL& path, int64 delta) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
FileSystemURL url(context_->CrackURL(path));
if (!url.is_valid())
return;
const UpdateObserverList* observers =
context_->GetUpdateObservers(url.type());
if (!observers)
return;
observers->Notify(&FileUpdateObserver::OnUpdate, MakeTuple(url, delta));
observers->Notify(&FileUpdateObserver::OnEndUpdate, MakeTuple(url));
}
void FileAPIMessageFilter::OnSyncGetPlatformPath(
const GURL& path, base::FilePath* platform_path) {
SyncGetPlatformPath(context_, process_id_, path, platform_path);
}
void FileAPIMessageFilter::OnCreateSnapshotFile(
int request_id, const GURL& path) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
FileSystemURL url(context_->CrackURL(path));
// Make sure if this file can be read by the renderer as this is
// called when the renderer is about to create a new File object
// (for reading the file).
if (!ValidateFileSystemURL(request_id, url))
return;
if (!security_policy_->CanReadFileSystemFile(process_id_, url)) {
Send(new FileSystemMsg_DidFail(request_id,
base::PLATFORM_FILE_ERROR_SECURITY));
return;
}
operations_[request_id] = operation_runner()->CreateSnapshotFile(
url,
base::Bind(&FileAPIMessageFilter::DidCreateSnapshot,
this, request_id, url));
}
void FileAPIMessageFilter::OnDidReceiveSnapshotFile(int request_id) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
in_transit_snapshot_files_.erase(request_id);
}
void FileAPIMessageFilter::OnStartBuildingBlob(const std::string& uuid) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
ignore_result(blob_storage_host_->StartBuildingBlob(uuid));
}
void FileAPIMessageFilter::OnAppendBlobDataItemToBlob(
const std::string& uuid, const BlobData::Item& item) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
if (item.type() == BlobData::Item::TYPE_FILE_FILESYSTEM) {
FileSystemURL filesystem_url(context_->CrackURL(item.filesystem_url()));
if (!FileSystemURLIsValid(context_, filesystem_url) ||
!security_policy_->CanReadFileSystemFile(process_id_, filesystem_url)) {
ignore_result(blob_storage_host_->CancelBuildingBlob(uuid));
return;
}
}
if (item.type() == BlobData::Item::TYPE_FILE &&
!security_policy_->CanReadFile(process_id_, item.path())) {
ignore_result(blob_storage_host_->CancelBuildingBlob(uuid));
return;
}
if (item.length() == 0) {
BadMessageReceived();
return;
}
ignore_result(blob_storage_host_->AppendBlobDataItem(uuid, item));
}
void FileAPIMessageFilter::OnAppendSharedMemoryToBlob(
const std::string& uuid,
base::SharedMemoryHandle handle,
size_t buffer_size) {
DCHECK(base::SharedMemory::IsHandleValid(handle));
if (!buffer_size) {
BadMessageReceived();
return;
}
#if defined(OS_WIN)
base::SharedMemory shared_memory(handle, true, PeerHandle());
#else
base::SharedMemory shared_memory(handle, true);
#endif
if (!shared_memory.Map(buffer_size)) {
ignore_result(blob_storage_host_->CancelBuildingBlob(uuid));
return;
}
BlobData::Item item;
item.SetToSharedBytes(static_cast<char*>(shared_memory.memory()),
buffer_size);
ignore_result(blob_storage_host_->AppendBlobDataItem(uuid, item));
}
void FileAPIMessageFilter::OnFinishBuildingBlob(
const std::string& uuid, const std::string& content_type) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
ignore_result(blob_storage_host_->FinishBuildingBlob(uuid, content_type));
// TODO(michaeln): check return values once blink has migrated, crbug/174200
}
void FileAPIMessageFilter::OnIncrementBlobRefCount(const std::string& uuid) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
ignore_result(blob_storage_host_->IncrementBlobRefCount(uuid));
}
void FileAPIMessageFilter::OnDecrementBlobRefCount(const std::string& uuid) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
ignore_result(blob_storage_host_->DecrementBlobRefCount(uuid));
}
void FileAPIMessageFilter::OnRegisterPublicBlobURL(
const GURL& public_url, const std::string& uuid) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
ignore_result(blob_storage_host_->RegisterPublicBlobURL(public_url, uuid));
}
void FileAPIMessageFilter::OnRevokePublicBlobURL(const GURL& public_url) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
ignore_result(blob_storage_host_->RevokePublicBlobURL(public_url));
}
void FileAPIMessageFilter::OnStartBuildingStream(
const GURL& url, const std::string& content_type) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
// Only an internal Blob URL is expected here. See the BlobURL of the Blink.
if (!StartsWithASCII(
url.path(), "blobinternal%3A///", true /* case_sensitive */)) {
NOTREACHED() << "Malformed Stream URL: " << url.spec();
BadMessageReceived();
return;
}
// Use an empty security origin for now. Stream accepts a security origin
// but how it's handled is not fixed yet.
new Stream(stream_context_->registry(),
NULL /* write_observer */,
url);
stream_urls_.insert(url.spec());
}
void FileAPIMessageFilter::OnAppendBlobDataItemToStream(
const GURL& url, const BlobData::Item& item) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
scoped_refptr<Stream> stream(GetStreamForURL(url));
// Stream instances may be deleted on error. Just abort if there's no Stream
// instance for |url| in the registry.
if (!stream.get())
return;
// Data for stream is delivered as TYPE_BYTES item.
if (item.type() != BlobData::Item::TYPE_BYTES) {
BadMessageReceived();
return;
}
stream->AddData(item.bytes(), item.length());
}
void FileAPIMessageFilter::OnAppendSharedMemoryToStream(
const GURL& url, base::SharedMemoryHandle handle, size_t buffer_size) {
DCHECK(base::SharedMemory::IsHandleValid(handle));
if (!buffer_size) {
BadMessageReceived();
return;
}
#if defined(OS_WIN)
base::SharedMemory shared_memory(handle, true, PeerHandle());
#else
base::SharedMemory shared_memory(handle, true);
#endif
if (!shared_memory.Map(buffer_size)) {
OnRemoveStream(url);
return;
}
scoped_refptr<Stream> stream(GetStreamForURL(url));
if (!stream.get())
return;
stream->AddData(static_cast<char*>(shared_memory.memory()), buffer_size);
}
void FileAPIMessageFilter::OnFinishBuildingStream(const GURL& url) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
scoped_refptr<Stream> stream(GetStreamForURL(url));
if (stream.get())
stream->Finalize();
}
void FileAPIMessageFilter::OnAbortBuildingStream(const GURL& url) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
scoped_refptr<Stream> stream(GetStreamForURL(url));
if (stream.get())
stream->Abort();
}
void FileAPIMessageFilter::OnCloneStream(
const GURL& url, const GURL& src_url) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
// Abort if there's no Stream instance for |src_url| (source Stream which
// we're going to make |url| point to) in the registry.
if (!GetStreamForURL(src_url))
return;
stream_context_->registry()->CloneStream(url, src_url);
stream_urls_.insert(url.spec());
}
void FileAPIMessageFilter::OnRemoveStream(const GURL& url) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
if (!GetStreamForURL(url).get())
return;
stream_context_->registry()->UnregisterStream(url);
stream_urls_.erase(url.spec());
}
void FileAPIMessageFilter::DidFinish(int request_id,
base::PlatformFileError result) {
if (result == base::PLATFORM_FILE_OK)
Send(new FileSystemMsg_DidSucceed(request_id));
else
Send(new FileSystemMsg_DidFail(request_id, result));
operations_.erase(request_id);
}
void FileAPIMessageFilter::DidGetMetadata(
int request_id,
base::PlatformFileError result,
const base::PlatformFileInfo& info) {
if (result == base::PLATFORM_FILE_OK)
Send(new FileSystemMsg_DidReadMetadata(request_id, info));
else
Send(new FileSystemMsg_DidFail(request_id, result));
operations_.erase(request_id);
}
void FileAPIMessageFilter::DidReadDirectory(
int request_id,
base::PlatformFileError result,
const std::vector<fileapi::DirectoryEntry>& entries,
bool has_more) {
if (result == base::PLATFORM_FILE_OK)
Send(new FileSystemMsg_DidReadDirectory(request_id, entries, has_more));
else
Send(new FileSystemMsg_DidFail(request_id, result));
operations_.erase(request_id);
}
void FileAPIMessageFilter::DidOpenFile(int request_id,
quota::QuotaLimitType quota_policy,
base::PlatformFileError result,
base::PlatformFile file,
const base::Closure& on_close_callback,
base::ProcessHandle peer_handle) {
if (result == base::PLATFORM_FILE_OK) {
IPC::PlatformFileForTransit file_for_transit =
file != base::kInvalidPlatformFileValue ?
IPC::GetFileHandleForProcess(file, peer_handle, true) :
IPC::InvalidPlatformFileForTransit();
int file_open_id = on_close_callbacks_.Add(
new base::Closure(on_close_callback));
Send(new FileSystemMsg_DidOpenFile(request_id,
file_for_transit,
file_open_id,
quota_policy));
} else {
Send(new FileSystemMsg_DidFail(request_id,
result));
}
operations_.erase(request_id);
}
void FileAPIMessageFilter::DidWrite(int request_id,
base::PlatformFileError result,
int64 bytes,
bool complete) {
if (result == base::PLATFORM_FILE_OK) {
Send(new FileSystemMsg_DidWrite(request_id, bytes, complete));
if (complete)
operations_.erase(request_id);
} else {
Send(new FileSystemMsg_DidFail(request_id, result));
operations_.erase(request_id);
}
}
void FileAPIMessageFilter::DidOpenFileSystem(int request_id,
const GURL& root,
const std::string& filesystem_name,
base::PlatformFileError result) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
if (result == base::PLATFORM_FILE_OK) {
DCHECK(root.is_valid());
Send(new FileSystemMsg_DidOpenFileSystem(
request_id, filesystem_name, root));
} else {
Send(new FileSystemMsg_DidFail(request_id, result));
}
// For OpenFileSystem we do not create a new operation, so no unregister here.
}
void FileAPIMessageFilter::DidResolveURL(int request_id,
base::PlatformFileError result,
const fileapi::FileSystemInfo& info,
const base::FilePath& file_path,
bool is_directory) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
if (result == base::PLATFORM_FILE_OK) {
DCHECK(info.root_url.is_valid());
Send(new FileSystemMsg_DidResolveURL(
request_id, info, file_path, is_directory));
} else {
Send(new FileSystemMsg_DidFail(request_id, result));
}
// For ResolveURL we do not create a new operation, so no unregister here.
}
void FileAPIMessageFilter::DidDeleteFileSystem(
int request_id,
base::PlatformFileError result) {
if (result == base::PLATFORM_FILE_OK)
Send(new FileSystemMsg_DidSucceed(request_id));
else
Send(new FileSystemMsg_DidFail(request_id, result));
// For DeleteFileSystem we do not create a new operation,
// so no unregister here.
}
void FileAPIMessageFilter::DidCreateSnapshot(
int request_id,
const fileapi::FileSystemURL& url,
base::PlatformFileError result,
const base::PlatformFileInfo& info,
const base::FilePath& platform_path,
const scoped_refptr<webkit_blob::ShareableFileReference>& /* unused */) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
operations_.erase(request_id);
if (result != base::PLATFORM_FILE_OK) {
Send(new FileSystemMsg_DidFail(request_id, result));
return;
}
scoped_refptr<webkit_blob::ShareableFileReference> file_ref =
webkit_blob::ShareableFileReference::Get(platform_path);
if (!security_policy_->CanReadFile(process_id_, platform_path)) {
// Give per-file read permission to the snapshot file if it hasn't it yet.
// In order for the renderer to be able to read the file via File object,
// it must be granted per-file read permission for the file's platform
// path. By now, it has already been verified that the renderer has
// sufficient permissions to read the file, so giving per-file permission
// here must be safe.
security_policy_->GrantReadFile(process_id_, platform_path);
// Revoke all permissions for the file when the last ref of the file
// is dropped.
if (!file_ref.get()) {
// Create a reference for temporary permission handling.
file_ref = webkit_blob::ShareableFileReference::GetOrCreate(
platform_path,
webkit_blob::ShareableFileReference::DONT_DELETE_ON_FINAL_RELEASE,
context_->default_file_task_runner());
}
file_ref->AddFinalReleaseCallback(
base::Bind(&RevokeFilePermission, process_id_));
}
if (file_ref.get()) {
// This ref is held until OnDidReceiveSnapshotFile is called.
in_transit_snapshot_files_[request_id] = file_ref;
}
// Return the file info and platform_path.
Send(new FileSystemMsg_DidCreateSnapshotFile(
request_id, info, platform_path));
}
bool FileAPIMessageFilter::ValidateFileSystemURL(
int request_id, const fileapi::FileSystemURL& url) {
if (FileSystemURLIsValid(context_, url))
return true;
Send(new FileSystemMsg_DidFail(request_id,
base::PLATFORM_FILE_ERROR_INVALID_URL));
return false;
}
scoped_refptr<Stream> FileAPIMessageFilter::GetStreamForURL(const GURL& url) {
return stream_context_->registry()->GetStream(url);
}
} // namespace content