This source file includes following definitions.
- DummyGetDataBuffer
- DoClose
- DoWork
- bytes_to_read_
- DoWork
- append_
- DoWork
- called_close_
- AsPPB_FileIO_API
- Open
- Query
- Touch
- Read
- ReadToArray
- Write
- SetLength
- Flush
- GetMaxWrittenOffset
- GetAppendModeWriteAmount
- SetMaxWrittenOffset
- SetAppendModeWriteAmount
- Close
- RequestOSFileHandle
- IsValid
- ReadValidated
- WriteValidated
- SetLengthValidated
- OnQueryComplete
- OnReadComplete
- OnRequestWriteQuotaComplete
- OnRequestSetLengthQuotaComplete
- OnWriteComplete
- OnPluginMsgGeneralComplete
- OnPluginMsgOpenFileComplete
- OnPluginMsgRequestOSFileHandleComplete
#include "ppapi/proxy/file_io_resource.h"
#include "base/bind.h"
#include "base/task_runner_util.h"
#include "ipc/ipc_message.h"
#include "ppapi/c/pp_errors.h"
#include "ppapi/proxy/ppapi_messages.h"
#include "ppapi/shared_impl/array_writer.h"
#include "ppapi/shared_impl/file_ref_create_info.h"
#include "ppapi/shared_impl/file_system_util.h"
#include "ppapi/shared_impl/file_type_conversion.h"
#include "ppapi/shared_impl/ppapi_globals.h"
#include "ppapi/shared_impl/proxy_lock.h"
#include "ppapi/shared_impl/resource_tracker.h"
#include "ppapi/thunk/enter.h"
#include "ppapi/thunk/ppb_file_ref_api.h"
#include "ppapi/thunk/ppb_file_system_api.h"
using ppapi::thunk::EnterResourceNoLock;
using ppapi::thunk::PPB_FileIO_API;
using ppapi::thunk::PPB_FileRef_API;
using ppapi::thunk::PPB_FileSystem_API;
namespace {
static const int32_t kMaxReadWriteSize = 32 * 1024 * 1024;
void* DummyGetDataBuffer(void* user_data, uint32_t count, uint32_t size) {
return user_data;
}
void DoClose(base::PlatformFile file) {
base::ClosePlatformFile(file);
}
}
namespace ppapi {
namespace proxy {
FileIOResource::QueryOp::QueryOp(scoped_refptr<FileHandleHolder> file_handle)
: file_handle_(file_handle) {
DCHECK(file_handle_);
}
FileIOResource::QueryOp::~QueryOp() {
}
int32_t FileIOResource::QueryOp::DoWork() {
base::File file(file_handle_->raw_handle());
bool success = file.GetInfo(&file_info_);
file.TakePlatformFile();
return success ? PP_OK : PP_ERROR_FAILED;
}
FileIOResource::ReadOp::ReadOp(scoped_refptr<FileHandleHolder> file_handle,
int64_t offset,
int32_t bytes_to_read)
: file_handle_(file_handle),
offset_(offset),
bytes_to_read_(bytes_to_read) {
DCHECK(file_handle_);
}
FileIOResource::ReadOp::~ReadOp() {
}
int32_t FileIOResource::ReadOp::DoWork() {
DCHECK(!buffer_.get());
buffer_.reset(new char[bytes_to_read_]);
return base::ReadPlatformFile(
file_handle_->raw_handle(), offset_, buffer_.get(), bytes_to_read_);
}
FileIOResource::WriteOp::WriteOp(scoped_refptr<FileHandleHolder> file_handle,
int64_t offset,
const char* buffer,
int32_t bytes_to_write,
bool append)
: file_handle_(file_handle),
offset_(offset),
buffer_(buffer),
bytes_to_write_(bytes_to_write),
append_(append) {
}
FileIOResource::WriteOp::~WriteOp() {
}
int32_t FileIOResource::WriteOp::DoWork() {
if (append_) {
return base::WritePlatformFileAtCurrentPos(
file_handle_->raw_handle(), buffer_, bytes_to_write_);
} else {
return base::WritePlatformFile(
file_handle_->raw_handle(), offset_, buffer_, bytes_to_write_);
}
}
FileIOResource::FileIOResource(Connection connection, PP_Instance instance)
: PluginResource(connection, instance),
file_system_type_(PP_FILESYSTEMTYPE_INVALID),
open_flags_(0),
max_written_offset_(0),
append_mode_write_amount_(0),
check_quota_(false),
called_close_(false) {
SendCreate(BROWSER, PpapiHostMsg_FileIO_Create());
}
FileIOResource::~FileIOResource() {
Close();
}
PPB_FileIO_API* FileIOResource::AsPPB_FileIO_API() {
return this;
}
int32_t FileIOResource::Open(PP_Resource file_ref,
int32_t open_flags,
scoped_refptr<TrackedCallback> callback) {
EnterResourceNoLock<PPB_FileRef_API> enter_file_ref(file_ref, true);
if (enter_file_ref.failed())
return PP_ERROR_BADRESOURCE;
PPB_FileRef_API* file_ref_api = enter_file_ref.object();
const FileRefCreateInfo& create_info = file_ref_api->GetCreateInfo();
if (!FileSystemTypeIsValid(create_info.file_system_type)) {
NOTREACHED();
return PP_ERROR_FAILED;
}
int32_t rv = state_manager_.CheckOperationState(
FileIOStateManager::OPERATION_EXCLUSIVE, false);
if (rv != PP_OK)
return rv;
open_flags_ = open_flags;
file_system_type_ = create_info.file_system_type;
if (create_info.file_system_plugin_resource) {
EnterResourceNoLock<PPB_FileSystem_API> enter_file_system(
create_info.file_system_plugin_resource, true);
if (enter_file_system.failed())
return PP_ERROR_FAILED;
file_system_resource_ = enter_file_system.resource();
}
file_ref_ = enter_file_ref.resource();
Call<PpapiPluginMsg_FileIO_OpenReply>(BROWSER,
PpapiHostMsg_FileIO_Open(
file_ref,
open_flags),
base::Bind(&FileIOResource::OnPluginMsgOpenFileComplete, this,
callback));
state_manager_.SetPendingOperation(FileIOStateManager::OPERATION_EXCLUSIVE);
return PP_OK_COMPLETIONPENDING;
}
int32_t FileIOResource::Query(PP_FileInfo* info,
scoped_refptr<TrackedCallback> callback) {
int32_t rv = state_manager_.CheckOperationState(
FileIOStateManager::OPERATION_EXCLUSIVE, true);
if (rv != PP_OK)
return rv;
if (!info)
return PP_ERROR_BADARGUMENT;
if (!FileHandleHolder::IsValid(file_handle_))
return PP_ERROR_FAILED;
state_manager_.SetPendingOperation(FileIOStateManager::OPERATION_EXCLUSIVE);
if (callback->is_blocking()) {
int32_t result = PP_ERROR_FAILED;
base::File::Info file_info;
scoped_refptr<FileIOResource> protect(this);
{
ProxyAutoUnlock unlock;
base::File file(file_handle_->raw_handle());
bool success = file.GetInfo(&file_info);
file.TakePlatformFile();
if (success)
result = PP_OK;
}
if (result == PP_OK) {
ppapi::FileInfoToPepperFileInfo(file_info,
file_system_type_,
info);
}
state_manager_.SetOperationFinished();
return result;
}
scoped_refptr<QueryOp> query_op(new QueryOp(file_handle_));
base::PostTaskAndReplyWithResult(
PpapiGlobals::Get()->GetFileTaskRunner(),
FROM_HERE,
Bind(&FileIOResource::QueryOp::DoWork, query_op),
RunWhileLocked(Bind(&TrackedCallback::Run, callback)));
callback->set_completion_task(
Bind(&FileIOResource::OnQueryComplete, this, query_op, info));
return PP_OK_COMPLETIONPENDING;
}
int32_t FileIOResource::Touch(PP_Time last_access_time,
PP_Time last_modified_time,
scoped_refptr<TrackedCallback> callback) {
int32_t rv = state_manager_.CheckOperationState(
FileIOStateManager::OPERATION_EXCLUSIVE, true);
if (rv != PP_OK)
return rv;
Call<PpapiPluginMsg_FileIO_GeneralReply>(BROWSER,
PpapiHostMsg_FileIO_Touch(last_access_time, last_modified_time),
base::Bind(&FileIOResource::OnPluginMsgGeneralComplete, this,
callback));
state_manager_.SetPendingOperation(FileIOStateManager::OPERATION_EXCLUSIVE);
return PP_OK_COMPLETIONPENDING;
}
int32_t FileIOResource::Read(int64_t offset,
char* buffer,
int32_t bytes_to_read,
scoped_refptr<TrackedCallback> callback) {
int32_t rv = state_manager_.CheckOperationState(
FileIOStateManager::OPERATION_READ, true);
if (rv != PP_OK)
return rv;
PP_ArrayOutput output_adapter;
output_adapter.GetDataBuffer = &DummyGetDataBuffer;
output_adapter.user_data = buffer;
return ReadValidated(offset, bytes_to_read, output_adapter, callback);
}
int32_t FileIOResource::ReadToArray(int64_t offset,
int32_t max_read_length,
PP_ArrayOutput* array_output,
scoped_refptr<TrackedCallback> callback) {
DCHECK(array_output);
int32_t rv = state_manager_.CheckOperationState(
FileIOStateManager::OPERATION_READ, true);
if (rv != PP_OK)
return rv;
return ReadValidated(offset, max_read_length, *array_output, callback);
}
int32_t FileIOResource::Write(int64_t offset,
const char* buffer,
int32_t bytes_to_write,
scoped_refptr<TrackedCallback> callback) {
if (!buffer)
return PP_ERROR_FAILED;
if (offset < 0 || bytes_to_write < 0)
return PP_ERROR_FAILED;
if (!FileHandleHolder::IsValid(file_handle_))
return PP_ERROR_FAILED;
int32_t rv = state_manager_.CheckOperationState(
FileIOStateManager::OPERATION_WRITE, true);
if (rv != PP_OK)
return rv;
state_manager_.SetPendingOperation(FileIOStateManager::OPERATION_WRITE);
if (check_quota_) {
int64_t increase = 0;
uint64_t max_offset = 0;
bool append = (open_flags_ & PP_FILEOPENFLAG_APPEND) != 0;
if (append) {
increase = bytes_to_write;
} else {
uint64_t max_offset = offset + bytes_to_write;
if (max_offset > static_cast<uint64_t>(kint64max))
return PP_ERROR_FAILED;
increase = static_cast<int64_t>(max_offset) - max_written_offset_;
}
if (increase > 0) {
int64_t result =
file_system_resource_->AsPPB_FileSystem_API()->RequestQuota(
increase,
base::Bind(&FileIOResource::OnRequestWriteQuotaComplete,
this,
offset, buffer, bytes_to_write, callback));
if (result == PP_OK_COMPLETIONPENDING)
return PP_OK_COMPLETIONPENDING;
DCHECK(result == increase);
if (append)
append_mode_write_amount_ += bytes_to_write;
else
max_written_offset_ = max_offset;
}
}
return WriteValidated(offset, buffer, bytes_to_write, callback);
}
int32_t FileIOResource::SetLength(int64_t length,
scoped_refptr<TrackedCallback> callback) {
int32_t rv = state_manager_.CheckOperationState(
FileIOStateManager::OPERATION_EXCLUSIVE, true);
if (rv != PP_OK)
return rv;
if (length < 0)
return PP_ERROR_FAILED;
if (check_quota_) {
int64_t increase = length - max_written_offset_;
if (increase > 0) {
int32_t result =
file_system_resource_->AsPPB_FileSystem_API()->RequestQuota(
increase,
base::Bind(&FileIOResource::OnRequestSetLengthQuotaComplete,
this,
length, callback));
if (result == PP_OK_COMPLETIONPENDING) {
state_manager_.SetPendingOperation(
FileIOStateManager::OPERATION_EXCLUSIVE);
return PP_OK_COMPLETIONPENDING;
}
DCHECK(result == increase);
max_written_offset_ = length;
}
}
state_manager_.SetPendingOperation(FileIOStateManager::OPERATION_EXCLUSIVE);
SetLengthValidated(length, callback);
return PP_OK_COMPLETIONPENDING;
}
int32_t FileIOResource::Flush(scoped_refptr<TrackedCallback> callback) {
int32_t rv = state_manager_.CheckOperationState(
FileIOStateManager::OPERATION_EXCLUSIVE, true);
if (rv != PP_OK)
return rv;
Call<PpapiPluginMsg_FileIO_GeneralReply>(BROWSER,
PpapiHostMsg_FileIO_Flush(),
base::Bind(&FileIOResource::OnPluginMsgGeneralComplete, this,
callback));
state_manager_.SetPendingOperation(FileIOStateManager::OPERATION_EXCLUSIVE);
return PP_OK_COMPLETIONPENDING;
}
int64_t FileIOResource::GetMaxWrittenOffset() const {
return max_written_offset_;
}
int64_t FileIOResource::GetAppendModeWriteAmount() const {
return append_mode_write_amount_;
}
void FileIOResource::SetMaxWrittenOffset(int64_t max_written_offset) {
max_written_offset_ = max_written_offset;
}
void FileIOResource::SetAppendModeWriteAmount(
int64_t append_mode_write_amount) {
append_mode_write_amount_ = append_mode_write_amount;
}
void FileIOResource::Close() {
if (called_close_)
return;
called_close_ = true;
if (check_quota_) {
check_quota_ = false;
file_system_resource_->AsPPB_FileSystem_API()->CloseQuotaFile(
pp_resource());
}
if (file_handle_)
file_handle_ = NULL;
Post(BROWSER, PpapiHostMsg_FileIO_Close(
FileGrowth(max_written_offset_, append_mode_write_amount_)));
}
int32_t FileIOResource::RequestOSFileHandle(
PP_FileHandle* handle,
scoped_refptr<TrackedCallback> callback) {
int32_t rv = state_manager_.CheckOperationState(
FileIOStateManager::OPERATION_EXCLUSIVE, true);
if (rv != PP_OK)
return rv;
Call<PpapiPluginMsg_FileIO_RequestOSFileHandleReply>(BROWSER,
PpapiHostMsg_FileIO_RequestOSFileHandle(),
base::Bind(&FileIOResource::OnPluginMsgRequestOSFileHandleComplete, this,
callback, handle));
state_manager_.SetPendingOperation(FileIOStateManager::OPERATION_EXCLUSIVE);
return PP_OK_COMPLETIONPENDING;
}
FileIOResource::FileHandleHolder::FileHandleHolder(PP_FileHandle file_handle)
: raw_handle_(file_handle) {
}
bool FileIOResource::FileHandleHolder::IsValid(
const scoped_refptr<FileIOResource::FileHandleHolder>& handle) {
return handle && (handle->raw_handle() != base::kInvalidPlatformFileValue);
}
FileIOResource::FileHandleHolder::~FileHandleHolder() {
if (raw_handle_ != base::kInvalidPlatformFileValue) {
base::TaskRunner* file_task_runner =
PpapiGlobals::Get()->GetFileTaskRunner();
file_task_runner->PostTask(FROM_HERE,
base::Bind(&DoClose, raw_handle_));
}
}
int32_t FileIOResource::ReadValidated(int64_t offset,
int32_t bytes_to_read,
const PP_ArrayOutput& array_output,
scoped_refptr<TrackedCallback> callback) {
if (bytes_to_read < 0)
return PP_ERROR_FAILED;
if (!FileHandleHolder::IsValid(file_handle_))
return PP_ERROR_FAILED;
state_manager_.SetPendingOperation(FileIOStateManager::OPERATION_READ);
bytes_to_read = std::min(bytes_to_read, kMaxReadWriteSize);
if (callback->is_blocking()) {
char* buffer = static_cast<char*>(
array_output.GetDataBuffer(array_output.user_data, bytes_to_read, 1));
int32_t result = PP_ERROR_FAILED;
scoped_refptr<FileIOResource> protect(this);
if (buffer) {
ProxyAutoUnlock unlock;
result = base::ReadPlatformFile(
file_handle_->raw_handle(), offset, buffer, bytes_to_read);
if (result < 0)
result = PP_ERROR_FAILED;
}
state_manager_.SetOperationFinished();
return result;
}
scoped_refptr<ReadOp> read_op(
new ReadOp(file_handle_, offset, bytes_to_read));
base::PostTaskAndReplyWithResult(
PpapiGlobals::Get()->GetFileTaskRunner(),
FROM_HERE,
Bind(&FileIOResource::ReadOp::DoWork, read_op),
RunWhileLocked(Bind(&TrackedCallback::Run, callback)));
callback->set_completion_task(
Bind(&FileIOResource::OnReadComplete, this, read_op, array_output));
return PP_OK_COMPLETIONPENDING;
}
int32_t FileIOResource::WriteValidated(
int64_t offset,
const char* buffer,
int32_t bytes_to_write,
scoped_refptr<TrackedCallback> callback) {
bool append = (open_flags_ & PP_FILEOPENFLAG_APPEND) != 0;
if (callback->is_blocking()) {
int32_t result;
{
ProxyAutoUnlock unlock;
if (append) {
result = base::WritePlatformFileAtCurrentPos(
file_handle_->raw_handle(), buffer, bytes_to_write);
} else {
result = base::WritePlatformFile(
file_handle_->raw_handle(), offset, buffer, bytes_to_write);
}
}
if (result < 0)
result = PP_ERROR_FAILED;
state_manager_.SetOperationFinished();
return result;
}
scoped_refptr<WriteOp> write_op(
new WriteOp(file_handle_, offset, buffer, bytes_to_write, append));
base::PostTaskAndReplyWithResult(
PpapiGlobals::Get()->GetFileTaskRunner(),
FROM_HERE,
Bind(&FileIOResource::WriteOp::DoWork, write_op),
RunWhileLocked(Bind(&TrackedCallback::Run, callback)));
callback->set_completion_task(
Bind(&FileIOResource::OnWriteComplete, this, write_op));
return PP_OK_COMPLETIONPENDING;
}
void FileIOResource::SetLengthValidated(
int64_t length,
scoped_refptr<TrackedCallback> callback) {
Call<PpapiPluginMsg_FileIO_GeneralReply>(BROWSER,
PpapiHostMsg_FileIO_SetLength(length),
base::Bind(&FileIOResource::OnPluginMsgGeneralComplete, this,
callback));
if (max_written_offset_ < length)
max_written_offset_ = length;
}
int32_t FileIOResource::OnQueryComplete(scoped_refptr<QueryOp> query_op,
PP_FileInfo* info,
int32_t result) {
DCHECK(state_manager_.get_pending_operation() ==
FileIOStateManager::OPERATION_EXCLUSIVE);
if (result == PP_OK) {
ppapi::FileInfoToPepperFileInfo(query_op->file_info(),
file_system_type_,
info);
}
state_manager_.SetOperationFinished();
return result;
}
int32_t FileIOResource::OnReadComplete(scoped_refptr<ReadOp> read_op,
PP_ArrayOutput array_output,
int32_t result) {
DCHECK(state_manager_.get_pending_operation() ==
FileIOStateManager::OPERATION_READ);
if (result >= 0) {
ArrayWriter output;
output.set_pp_array_output(array_output);
if (output.is_valid())
output.StoreArray(read_op->buffer(), result);
else
result = PP_ERROR_FAILED;
} else {
result = PP_ERROR_FAILED;
}
state_manager_.SetOperationFinished();
return result;
}
void FileIOResource::OnRequestWriteQuotaComplete(
int64_t offset,
const char* buffer,
int32_t bytes_to_write,
scoped_refptr<TrackedCallback> callback,
int64_t granted) {
DCHECK(granted >= 0);
if (granted == 0) {
callback->Run(PP_ERROR_NOQUOTA);
return;
}
if (open_flags_ & PP_FILEOPENFLAG_APPEND) {
DCHECK_LE(bytes_to_write, granted);
append_mode_write_amount_ += bytes_to_write;
} else {
DCHECK_LE(offset + bytes_to_write - max_written_offset_, granted);
int64_t max_offset = offset + bytes_to_write;
if (max_written_offset_ < max_offset)
max_written_offset_ = max_offset;
}
int32_t result = WriteValidated(offset, buffer, bytes_to_write, callback);
if (result != PP_OK_COMPLETIONPENDING)
callback->Run(result);
}
void FileIOResource::OnRequestSetLengthQuotaComplete(
int64_t length,
scoped_refptr<TrackedCallback> callback,
int64_t granted) {
DCHECK(granted >= 0);
if (granted == 0) {
callback->Run(PP_ERROR_NOQUOTA);
return;
}
DCHECK_LE(length - max_written_offset_, granted);
if (max_written_offset_ < length)
max_written_offset_ = length;
SetLengthValidated(length, callback);
}
int32_t FileIOResource::OnWriteComplete(scoped_refptr<WriteOp> write_op,
int32_t result) {
DCHECK(state_manager_.get_pending_operation() ==
FileIOStateManager::OPERATION_WRITE);
if (result < 0)
result = PP_ERROR_FAILED;
state_manager_.SetOperationFinished();
return result;
}
void FileIOResource::OnPluginMsgGeneralComplete(
scoped_refptr<TrackedCallback> callback,
const ResourceMessageReplyParams& params) {
DCHECK(state_manager_.get_pending_operation() ==
FileIOStateManager::OPERATION_EXCLUSIVE ||
state_manager_.get_pending_operation() ==
FileIOStateManager::OPERATION_WRITE);
state_manager_.SetOperationFinished();
callback->Run(params.result());
}
void FileIOResource::OnPluginMsgOpenFileComplete(
scoped_refptr<TrackedCallback> callback,
const ResourceMessageReplyParams& params,
PP_Resource quota_file_system,
int64_t max_written_offset) {
DCHECK(state_manager_.get_pending_operation() ==
FileIOStateManager::OPERATION_EXCLUSIVE);
file_ref_ = NULL;
int32_t result = params.result();
if (result == PP_OK) {
state_manager_.SetOpenSucceed();
if (quota_file_system) {
DCHECK(quota_file_system == file_system_resource_->pp_resource());
check_quota_ = true;
max_written_offset_ = max_written_offset;
file_system_resource_->AsPPB_FileSystem_API()->OpenQuotaFile(
pp_resource());
}
IPC::PlatformFileForTransit transit_file;
if (params.TakeFileHandleAtIndex(0, &transit_file)) {
file_handle_ = new FileHandleHolder(
IPC::PlatformFileForTransitToPlatformFile(transit_file));
}
}
state_manager_.SetOperationFinished();
callback->Run(result);
}
void FileIOResource::OnPluginMsgRequestOSFileHandleComplete(
scoped_refptr<TrackedCallback> callback,
PP_FileHandle* output_handle,
const ResourceMessageReplyParams& params) {
DCHECK(state_manager_.get_pending_operation() ==
FileIOStateManager::OPERATION_EXCLUSIVE);
if (!TrackedCallback::IsPending(callback)) {
state_manager_.SetOperationFinished();
return;
}
int32_t result = params.result();
IPC::PlatformFileForTransit transit_file;
if (!params.TakeFileHandleAtIndex(0, &transit_file))
result = PP_ERROR_FAILED;
*output_handle = IPC::PlatformFileForTransitToPlatformFile(transit_file);
state_manager_.SetOperationFinished();
callback->Run(result);
}
}
}