summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--components/nacl/loader/nacl_ipc_adapter.cc116
-rw-r--r--content/browser/renderer_host/pepper/pepper_file_io_host.cc227
-rw-r--r--content/browser/renderer_host/pepper/pepper_file_io_host.h28
-rw-r--r--content/browser/renderer_host/pepper/pepper_file_system_browser_host.cc125
-rw-r--r--content/browser/renderer_host/pepper/pepper_file_system_browser_host.h41
-rw-r--r--content/browser/renderer_host/pepper/quota_reservation.cc2
-rw-r--r--ppapi/ppapi_tests.gypi3
-rw-r--r--ppapi/proxy/file_io_resource.cc231
-rw-r--r--ppapi/proxy/file_io_resource.h53
-rw-r--r--ppapi/proxy/file_system_resource.cc109
-rw-r--r--ppapi/proxy/file_system_resource.h28
-rw-r--r--ppapi/proxy/nacl_message_scanner.cc269
-rw-r--r--ppapi/proxy/nacl_message_scanner.h66
-rw-r--r--ppapi/proxy/nacl_message_scanner_unittest.cc293
-rw-r--r--ppapi/proxy/ppapi_messages.h20
-rw-r--r--ppapi/thunk/ppb_file_io_api.h2
-rw-r--r--ppapi/thunk/ppb_file_system_api.h6
17 files changed, 1244 insertions, 375 deletions
diff --git a/components/nacl/loader/nacl_ipc_adapter.cc b/components/nacl/loader/nacl_ipc_adapter.cc
index c6aaf61..f591827 100644
--- a/components/nacl/loader/nacl_ipc_adapter.cc
+++ b/components/nacl/loader/nacl_ipc_adapter.cc
@@ -19,6 +19,8 @@
#include "native_client/src/trusted/desc/nacl_desc_custom.h"
#include "native_client/src/trusted/desc/nacl_desc_imc_shm.h"
#include "native_client/src/trusted/desc/nacl_desc_io.h"
+#include "native_client/src/trusted/desc/nacl_desc_quota.h"
+#include "native_client/src/trusted/desc/nacl_desc_quota_interface.h"
#include "native_client/src/trusted/desc/nacl_desc_sync_socket.h"
#include "native_client/src/trusted/desc/nacl_desc_wrapper.h"
#include "native_client/src/trusted/service_runtime/include/sys/fcntl.h"
@@ -26,6 +28,8 @@
#include "ppapi/proxy/ppapi_messages.h"
#include "ppapi/proxy/serialized_handle.h"
+using ppapi::proxy::NaClMessageScanner;
+
namespace {
enum BufferSizeStatus {
@@ -55,11 +59,12 @@ BufferSizeStatus GetBufferStatus(const char* data, size_t len) {
return MESSAGE_IS_TRUNCATED;
}
+//------------------------------------------------------------------------------
// This object allows the NaClDesc to hold a reference to a NaClIPCAdapter and
// forward calls to it.
struct DescThunker {
- explicit DescThunker(NaClIPCAdapter* adapter_param)
- : adapter(adapter_param) {
+ explicit DescThunker(NaClIPCAdapter* adapter_arg)
+ : adapter(adapter_arg) {
}
scoped_refptr<NaClIPCAdapter> adapter;
};
@@ -92,6 +97,90 @@ NaClDesc* MakeNaClDescCustom(NaClIPCAdapter* adapter) {
return NaClDescMakeCustomDesc(new DescThunker(adapter), &funcs);
}
+//------------------------------------------------------------------------------
+// This object is passed to a NaClDescQuota to intercept writes and forward them
+// to the NaClIPCAdapter, which checks quota. This is a NaCl-style struct. Don't
+// add non-trivial fields or virtual methods. Construction should use malloc,
+// because this is owned by the NaClDesc, and the NaCl Dtor code will call free.
+struct QuotaInterface {
+ // The "base" struct must be first. NaCl code expects a NaCl style ref-counted
+ // object, so the "vtable" and other base class fields must be first.
+ struct NaClDescQuotaInterface base NACL_IS_REFCOUNT_SUBCLASS;
+
+ NaClMessageScanner::FileIO* file_io;
+};
+
+static int64_t QuotaInterfaceWriteRequest(
+ NaClDescQuotaInterface* ndqi,
+ const uint8_t* /* unused_id */,
+ int64_t offset,
+ int64_t length) {
+ if (offset < 0 || length < 0)
+ return 0;
+ if (std::numeric_limits<int64_t>::max() - length < offset)
+ return 0; // offset + length would overflow.
+ int64_t max_offset = offset + length;
+ if (max_offset < 0)
+ return 0;
+
+ QuotaInterface* quota_interface = reinterpret_cast<QuotaInterface*>(ndqi);
+ NaClMessageScanner::FileIO* file_io = quota_interface->file_io;
+ int64_t increase = max_offset - file_io->max_written_offset();
+ if (increase <= 0 || file_io->Grow(increase))
+ return length;
+
+ return 0;
+}
+
+static int64_t QuotaInterfaceFtruncateRequest(
+ NaClDescQuotaInterface* ndqi,
+ const uint8_t* /* unused_id */,
+ int64_t length) {
+ // We can't implement SetLength on the plugin side due to sandbox limitations.
+ // See crbug.com/156077.
+ NOTREACHED();
+ return 0;
+}
+
+struct NaClDescQuotaInterfaceVtbl const kQuotaInterfaceVtbl = {
+ kNaClDescQuotaInterfaceVtbl.vbase, // NaClRefCountVtbl, containing Dtor.
+ QuotaInterfaceWriteRequest,
+ QuotaInterfaceFtruncateRequest
+};
+
+uint8_t unused_id[NACL_DESC_QUOTA_FILE_ID_LEN];
+
+NaClDesc* MakeNaClDescQuota(
+ NaClMessageScanner::FileIO* file_io,
+ NaClDesc* wrapped_desc) {
+ // Create the QuotaInterface.
+ QuotaInterface* quota_interface =
+ static_cast<QuotaInterface*>(malloc(sizeof *quota_interface));
+ if (quota_interface && NaClDescQuotaInterfaceCtor(&quota_interface->base)) {
+ quota_interface->base.base.vtbl =
+ (struct NaClRefCountVtbl *)(&kQuotaInterfaceVtbl);
+ // QuotaInterface is a trivial class, so skip the ctor.
+ quota_interface->file_io = file_io;
+ // Create the NaClDescQuota.
+ NaClDescQuota* desc = static_cast<NaClDescQuota*>(malloc(sizeof *desc));
+ if (desc && NaClDescQuotaCtor(desc,
+ wrapped_desc,
+ unused_id,
+ &quota_interface->base)) {
+ return &desc->base;
+ }
+ if (desc)
+ NaClDescUnref(reinterpret_cast<NaClDesc*>(desc));
+ }
+
+ if (quota_interface)
+ NaClDescQuotaInterfaceUnref(&quota_interface->base);
+
+ return NULL;
+}
+
+//------------------------------------------------------------------------------
+
void DeleteChannel(IPC::Channel* channel) {
delete channel;
}
@@ -432,21 +521,26 @@ bool NaClIPCAdapter::OnMessageReceived(const IPC::Message& msg) {
base::Passed(&response)));
break;
}
- case ppapi::proxy::SerializedHandle::FILE:
- // IMPORTANT: The NaClDescIoDescFromHandleAllocCtor function creates
- // a NaClDesc that checks file flags before reading and writing. This
- // is essential since PPB_FileIO now sends a file descriptor to the
- // plugin which may have write capabilities. We can't allow the plugin
- // to write with it since it could bypass quota checks, which still
- // happen in the host.
- nacl_desc.reset(new NaClDescWrapper(NaClDescIoDescFromHandleAllocCtor(
+ case ppapi::proxy::SerializedHandle::FILE: {
+ // Create the NaClDesc for the file descriptor. If quota checking is
+ // required, wrap it in a NaClDescQuota.
+ NaClDesc* desc = NaClDescIoDescFromHandleAllocCtor(
#if defined(OS_WIN)
iter->descriptor(),
#else
iter->descriptor().fd,
#endif
- TranslatePepperFileReadWriteOpenFlags(iter->open_flags()))));
+ TranslatePepperFileReadWriteOpenFlags(iter->open_flags()));
+ if (desc && iter->file_io()) {
+ desc = MakeNaClDescQuota(
+ locked_data_.nacl_msg_scanner_.GetFile(iter->file_io()),
+ desc);
+ }
+ if (desc)
+ nacl_desc.reset(new NaClDescWrapper(desc));
break;
+ }
+
case ppapi::proxy::SerializedHandle::INVALID: {
// Nothing to do. TODO(dmichael): Should we log this? Or is it
// sometimes okay to pass an INVALID handle?
diff --git a/content/browser/renderer_host/pepper/pepper_file_io_host.cc b/content/browser/renderer_host/pepper/pepper_file_io_host.cc
index 1118c58..6fe68de 100644
--- a/content/browser/renderer_host/pepper/pepper_file_io_host.cc
+++ b/content/browser/renderer_host/pepper/pepper_file_io_host.cc
@@ -41,13 +41,6 @@ using ppapi::PPTimeToTime;
namespace {
-int32_t ErrorOrByteNumber(int32_t pp_error, int32_t byte_number) {
- // On the plugin side, some callbacks expect a parameter that means different
- // things depending on whether it is negative or not. We translate for those
- // callbacks here.
- return pp_error == PP_OK ? byte_number : pp_error;
-}
-
PepperFileIOHost::UIThreadStuff
GetUIThreadStuffForInternalFileSystems(int render_process_id) {
PepperFileIOHost::UIThreadStuff stuff;
@@ -79,6 +72,10 @@ bool GetPluginAllowedToCallRequestOSFileHandle(int render_process_id,
host->GetBrowserContext(), document_url);
}
+bool FileOpenForWrite(int32_t open_flags) {
+ return (open_flags & (PP_FILEOPENFLAG_WRITE | PP_FILEOPENFLAG_APPEND)) != 0;
+}
+
} // namespace
PepperFileIOHost::PepperFileIOHost(BrowserPpapiHostImpl* host,
@@ -104,7 +101,9 @@ PepperFileIOHost::PepperFileIOHost(BrowserPpapiHostImpl* host,
}
PepperFileIOHost::~PepperFileIOHost() {
- OnHostMsgClose(NULL);
+ // FileIOResource will normally send a close message, but the plugin may have
+ // crashed.
+ OnHostMsgClose(NULL, max_written_offset_);
}
int32_t PepperFileIOHost::OnResourceMessageReceived(
@@ -115,14 +114,12 @@ int32_t PepperFileIOHost::OnResourceMessageReceived(
OnHostMsgOpen)
PPAPI_DISPATCH_HOST_RESOURCE_CALL(PpapiHostMsg_FileIO_Touch,
OnHostMsgTouch)
- PPAPI_DISPATCH_HOST_RESOURCE_CALL(PpapiHostMsg_FileIO_Write,
- OnHostMsgWrite)
PPAPI_DISPATCH_HOST_RESOURCE_CALL(PpapiHostMsg_FileIO_SetLength,
OnHostMsgSetLength)
PPAPI_DISPATCH_HOST_RESOURCE_CALL_0(PpapiHostMsg_FileIO_Flush,
OnHostMsgFlush)
- PPAPI_DISPATCH_HOST_RESOURCE_CALL_0(PpapiHostMsg_FileIO_Close,
- OnHostMsgClose)
+ PPAPI_DISPATCH_HOST_RESOURCE_CALL(PpapiHostMsg_FileIO_Close,
+ OnHostMsgClose)
PPAPI_DISPATCH_HOST_RESOURCE_CALL_0(PpapiHostMsg_FileIO_RequestOSFileHandle,
OnHostMsgRequestOSFileHandle)
IPC_END_MESSAGE_MAP()
@@ -209,13 +206,13 @@ void PepperFileIOHost::GotUIThreadStuffForInternalFileSystems(
if (resolved_render_process_id_ == base::kNullProcessId ||
!file_system_context_.get()) {
reply_context.params.set_result(PP_ERROR_FAILED);
- host()->SendReply(reply_context, PpapiPluginMsg_FileIO_OpenReply());
+ SendOpenErrorReply(reply_context);
return;
}
if (!file_system_context_->GetFileSystemBackend(file_system_url_.type())) {
reply_context.params.set_result(PP_ERROR_FAILED);
- host()->SendReply(reply_context, PpapiPluginMsg_FileIO_OpenReply());
+ SendOpenErrorReply(reply_context);
return;
}
@@ -237,8 +234,8 @@ void PepperFileIOHost::DidOpenInternalFile(
if (result == base::PLATFORM_FILE_OK) {
on_close_callback_ = on_close_callback;
- check_quota_ = file_system_host_ && file_system_host_->ChecksQuota();
- if (check_quota_) {
+ if (FileOpenForWrite(open_flags_) && file_system_host_->ChecksQuota()) {
+ check_quota_ = true;
file_system_host_->OpenQuotaFile(
this,
file_system_url_.path(),
@@ -293,55 +290,6 @@ int32_t PepperFileIOHost::OnHostMsgTouch(
return PP_OK_COMPLETIONPENDING;
}
-int32_t PepperFileIOHost::OnHostMsgWrite(
- ppapi::host::HostMessageContext* context,
- int64_t offset,
- const std::string& buffer) {
- int32_t rv = state_manager_.CheckOperationState(
- FileIOStateManager::OPERATION_WRITE, true);
- if (rv != PP_OK)
- return rv;
- if (offset < 0)
- return PP_ERROR_BADARGUMENT;
-
- if (check_quota_) {
- int64_t actual_offset =
- (open_flags_ & PP_FILEOPENFLAG_APPEND) ? max_written_offset_ : offset;
-
- uint64_t max_offset = actual_offset + buffer.size();
- if (max_offset > static_cast<uint64_t>(std::numeric_limits<int64_t>::max()))
- return PP_ERROR_FAILED; // max_offset overflows.
- int64_t amount = static_cast<int64_t>(max_offset) - max_written_offset_;
-
- // Quota request amounts are restricted to 32 bits so we can use atomics
- // when we move this code to the plugin side of the proxy.
- if (amount > std::numeric_limits<int32_t>::max())
- return PP_ERROR_NOQUOTA;
-
- if (amount > 0) {
- int32_t result = file_system_host_->RequestQuota(
- static_cast<int32_t>(amount),
- base::Bind(&PepperFileIOHost::GotWriteQuota,
- weak_factory_.GetWeakPtr(),
- context->MakeReplyMessageContext(),
- offset, buffer));
- if (result == PP_OK_COMPLETIONPENDING) {
- state_manager_.SetPendingOperation(FileIOStateManager::OPERATION_WRITE);
- return result;
- }
- // RequestQuota returns either PP_OK_COMPLETIONPENDING or the requested
- // quota amount.
- DCHECK(result > 0);
- }
- }
-
- if (!CallWrite(context->MakeReplyMessageContext(), offset, buffer))
- return PP_ERROR_FAILED;
-
- state_manager_.SetPendingOperation(FileIOStateManager::OPERATION_WRITE);
- return PP_OK_COMPLETIONPENDING;
-}
-
int32_t PepperFileIOHost::OnHostMsgSetLength(
ppapi::host::HostMessageContext* context,
int64_t length) {
@@ -352,32 +300,16 @@ int32_t PepperFileIOHost::OnHostMsgSetLength(
if (length < 0)
return PP_ERROR_BADARGUMENT;
- if (check_quota_) {
- int64_t amount = length - max_written_offset_;
- // Quota request amounts are restricted to 32 bits so we can use atomics
- // when we move this code to the plugin side of the proxy.
- if (amount > std::numeric_limits<int32_t>::max())
- return PP_ERROR_NOQUOTA;
-
- if (amount > 0) {
- int32_t result = file_system_host_->RequestQuota(
- static_cast<int32_t>(amount),
- base::Bind(&PepperFileIOHost::GotSetLengthQuota,
- weak_factory_.GetWeakPtr(),
- context->MakeReplyMessageContext(),
- length));
- if (result == PP_OK_COMPLETIONPENDING) {
- state_manager_.SetPendingOperation(
- FileIOStateManager::OPERATION_EXCLUSIVE);
- return result;
- }
- // RequestQuota returns either PP_OK_COMPLETIONPENDING or the requested
- // quota amount.
- DCHECK(result > 0);
- }
- }
+ // Quota checks are performed on the plugin side, in order to use the same
+ // quota reservation and request system as Write.
- if (!CallSetLength(context->MakeReplyMessageContext(), length))
+ if (!base::FileUtilProxy::Truncate(
+ file_message_loop_,
+ file_,
+ length,
+ base::Bind(&PepperFileIOHost::ExecutePlatformGeneralCallback,
+ weak_factory_.GetWeakPtr(),
+ context->MakeReplyMessageContext())))
return PP_ERROR_FAILED;
state_manager_.SetPendingOperation(FileIOStateManager::OPERATION_EXCLUSIVE);
@@ -404,9 +336,10 @@ int32_t PepperFileIOHost::OnHostMsgFlush(
}
int32_t PepperFileIOHost::OnHostMsgClose(
- ppapi::host::HostMessageContext* context) {
+ ppapi::host::HostMessageContext* context,
+ int64_t max_written_offset) {
if (check_quota_) {
- file_system_host_->CloseQuotaFile(this);
+ file_system_host_->CloseQuotaFile(this, max_written_offset);
check_quota_ = false;
}
@@ -426,75 +359,12 @@ void PepperFileIOHost::DidOpenQuotaFile(
base::PlatformFile file,
int64_t max_written_offset) {
max_written_offset_ = max_written_offset;
- DCHECK_LE(0, max_written_offset_);
ExecutePlatformOpenFileCallback(
reply_context, base::PLATFORM_FILE_OK, base::PassPlatformFile(&file),
true);
}
-void PepperFileIOHost::GotWriteQuota(
- ppapi::host::ReplyMessageContext reply_context,
- int64_t offset,
- const std::string& buffer,
- int32_t granted) {
- if (granted == 0) {
- reply_context.params.set_result(PP_ERROR_NOQUOTA);
- } else if (!CallWrite(reply_context, offset, buffer)) {
- reply_context.params.set_result(PP_ERROR_FAILED);
- } else {
- max_written_offset_ += granted;
- return;
- }
- // Return the error result set above.
- host()->SendReply(reply_context, PpapiPluginMsg_FileIO_GeneralReply());
- state_manager_.SetOperationFinished();
-}
-
-void PepperFileIOHost::GotSetLengthQuota(
- ppapi::host::ReplyMessageContext reply_context,
- int64_t length,
- int32_t granted) {
- if (granted == 0) {
- reply_context.params.set_result(PP_ERROR_NOQUOTA);
- } else if (!CallSetLength(reply_context, length)) {
- reply_context.params.set_result(PP_ERROR_FAILED);
- } else {
- max_written_offset_ += granted;
- return;
- }
- // Return the error result set above.
- host()->SendReply(reply_context, PpapiPluginMsg_FileIO_GeneralReply());
- state_manager_.SetOperationFinished();
-}
-
-bool PepperFileIOHost::CallWrite(
- ppapi::host::ReplyMessageContext reply_context,
- int64_t offset,
- const std::string& buffer) {
- return base::FileUtilProxy::Write(
- file_message_loop_,
- file_,
- offset,
- buffer.c_str(),
- buffer.size(),
- base::Bind(&PepperFileIOHost::ExecutePlatformWriteCallback,
- weak_factory_.GetWeakPtr(),
- reply_context));
-}
-
-bool PepperFileIOHost::CallSetLength(
- ppapi::host::ReplyMessageContext reply_context,
- int64_t length) {
- return base::FileUtilProxy::Truncate(
- file_message_loop_,
- file_,
- length,
- base::Bind(&PepperFileIOHost::ExecutePlatformGeneralCallback,
- weak_factory_.GetWeakPtr(),
- reply_context));
-}
-
void PepperFileIOHost::DidCloseFile(base::PlatformFileError error) {
// Silently ignore if we fail to close the file.
if (!on_close_callback_.is_null()) {
@@ -553,39 +423,31 @@ void PepperFileIOHost::ExecutePlatformOpenFileCallback(
base::PassPlatformFile file,
bool unused_created) {
int32_t pp_error = ppapi::PlatformFileErrorToPepperError(error_code);
- if (pp_error == PP_OK)
- state_manager_.SetOpenSucceed();
-
DCHECK(file_ == base::kInvalidPlatformFileValue);
file_ = file.ReleaseValue();
- if (file_ != base::kInvalidPlatformFileValue) {
- int32_t flags_to_send = open_flags_;
- if (!host()->permissions().HasPermission(ppapi::PERMISSION_DEV)) {
- // IMPORTANT: Clear PP_FILEOPENFLAG_WRITE and PP_FILEOPENFLAG_APPEND so
- // the plugin can't write and so bypass our quota checks.
- flags_to_send =
- open_flags_ & ~(PP_FILEOPENFLAG_WRITE | PP_FILEOPENFLAG_APPEND);
- }
- if (!AddFileToReplyContext(flags_to_send, &reply_context))
- pp_error = PP_ERROR_FAILED;
+ if (file_ != base::kInvalidPlatformFileValue &&
+ !AddFileToReplyContext(open_flags_, &reply_context))
+ pp_error = PP_ERROR_FAILED;
+
+ PP_Resource quota_file_system = 0;
+ if (pp_error == PP_OK) {
+ state_manager_.SetOpenSucceed();
+ // A non-zero resource id signals the plugin side to check quota.
+ if (check_quota_)
+ quota_file_system = file_system_host_->pp_resource();
}
+
reply_context.params.set_result(pp_error);
- host()->SendReply(reply_context, PpapiPluginMsg_FileIO_OpenReply());
+ host()->SendReply(reply_context,
+ PpapiPluginMsg_FileIO_OpenReply(quota_file_system,
+ max_written_offset_));
state_manager_.SetOperationFinished();
}
-void PepperFileIOHost::ExecutePlatformWriteCallback(
- ppapi::host::ReplyMessageContext reply_context,
- base::PlatformFileError error_code,
- int bytes_written) {
- // On the plugin side, the callback expects a parameter with different meaning
- // depends on whether is negative or not. It is the result here. We translate
- // for the callback.
- int32_t pp_error = ppapi::PlatformFileErrorToPepperError(error_code);
- reply_context.params.set_result(ErrorOrByteNumber(pp_error, bytes_written));
- host()->SendReply(reply_context, PpapiPluginMsg_FileIO_GeneralReply());
- state_manager_.SetOperationFinished();
+void PepperFileIOHost::SendOpenErrorReply(
+ ppapi::host::ReplyMessageContext reply_context) {
+ host()->SendReply(reply_context, PpapiPluginMsg_FileIO_OpenReply(0, 0));
}
bool PepperFileIOHost::AddFileToReplyContext(
@@ -602,8 +464,11 @@ bool PepperFileIOHost::AddFileToReplyContext(
file_, plugin_process_id, false);
if (transit_file == IPC::InvalidPlatformFileForTransit())
return false;
+
ppapi::proxy::SerializedHandle file_handle;
- file_handle.set_file_handle(transit_file, open_flags, 0 /* file_io */);
+ // A non-zero resource id signals NaClIPCAdapter to create a NaClQuotaDesc.
+ PP_Resource quota_file_io = check_quota_ ? pp_resource() : 0;
+ file_handle.set_file_handle(transit_file, open_flags, quota_file_io);
reply_context->params.AppendHandle(file_handle);
return true;
}
diff --git a/content/browser/renderer_host/pepper/pepper_file_io_host.h b/content/browser/renderer_host/pepper/pepper_file_io_host.h
index 9394e6c..4fa4bc2 100644
--- a/content/browser/renderer_host/pepper/pepper_file_io_host.h
+++ b/content/browser/renderer_host/pepper/pepper_file_io_host.h
@@ -41,12 +41,6 @@ class PepperFileIOHost : public ppapi::host::ResourceHost,
const IPC::Message& msg,
ppapi::host::HostMessageContext* context) OVERRIDE;
- // Direct access for PepperFileSystemBrowserHost.
- int64_t max_written_offset() const { return max_written_offset_; }
- void set_max_written_offset(int64_t max_written_offset) {
- max_written_offset_ = max_written_offset;
- }
-
struct UIThreadStuff {
UIThreadStuff();
~UIThreadStuff();
@@ -60,12 +54,10 @@ class PepperFileIOHost : public ppapi::host::ResourceHost,
int32_t OnHostMsgTouch(ppapi::host::HostMessageContext* context,
PP_Time last_access_time,
PP_Time last_modified_time);
- int32_t OnHostMsgWrite(ppapi::host::HostMessageContext* context,
- int64_t offset,
- const std::string& buffer);
int32_t OnHostMsgSetLength(ppapi::host::HostMessageContext* context,
int64_t length);
- int32_t OnHostMsgClose(ppapi::host::HostMessageContext* context);
+ int32_t OnHostMsgClose(ppapi::host::HostMessageContext* context,
+ int64_t max_written_offset);
int32_t OnHostMsgFlush(ppapi::host::HostMessageContext* context);
int32_t OnHostMsgRequestOSFileHandle(
ppapi::host::HostMessageContext* context);
@@ -86,10 +78,6 @@ class PepperFileIOHost : public ppapi::host::ResourceHost,
base::PlatformFileError error_code,
base::PassPlatformFile file,
bool unused_created);
- void ExecutePlatformWriteCallback(
- ppapi::host::ReplyMessageContext reply_context,
- base::PlatformFileError error_code,
- int bytes_written);
void GotUIThreadStuffForInternalFileSystems(
ppapi::host::ReplyMessageContext reply_context,
@@ -109,21 +97,13 @@ class PepperFileIOHost : public ppapi::host::ResourceHost,
void DidOpenQuotaFile(ppapi::host::ReplyMessageContext reply_context,
base::PlatformFile file,
int64_t max_written_offset);
- void GotWriteQuota(ppapi::host::ReplyMessageContext reply_context,
- int64_t offset,
- const std::string& buffer,
- int32_t granted);
- void GotSetLengthQuota(ppapi::host::ReplyMessageContext reply_context,
- int64_t length,
- int32_t granted);
- bool CallWrite(ppapi::host::ReplyMessageContext reply_context,
- int64_t offset,
- const std::string& buffer);
bool CallSetLength(ppapi::host::ReplyMessageContext reply_context,
int64_t length);
void DidCloseFile(base::PlatformFileError error);
+ void SendOpenErrorReply(ppapi::host::ReplyMessageContext reply_context);
+
// Adds file_ to |reply_context| with the specified |open_flags|.
bool AddFileToReplyContext(
int32_t open_flags,
diff --git a/content/browser/renderer_host/pepper/pepper_file_system_browser_host.cc b/content/browser/renderer_host/pepper/pepper_file_system_browser_host.cc
index 9161ad7..2bc0042 100644
--- a/content/browser/renderer_host/pepper/pepper_file_system_browser_host.cc
+++ b/content/browser/renderer_host/pepper/pepper_file_system_browser_host.cc
@@ -48,16 +48,6 @@ GetFileSystemContextFromRenderId(int render_process_id) {
} // namespace
-PepperFileSystemBrowserHost::QuotaRequest::QuotaRequest(
- int32_t amount_arg,
- const RequestQuotaCallback& callback_arg)
- : amount(amount_arg),
- callback(callback_arg) {
-}
-
-PepperFileSystemBrowserHost::QuotaRequest::~QuotaRequest() {
-}
-
PepperFileSystemBrowserHost::PepperFileSystemBrowserHost(BrowserPpapiHost* host,
PP_Instance instance,
PP_Resource resource,
@@ -110,6 +100,9 @@ int32_t PepperFileSystemBrowserHost::OnResourceMessageReceived(
PPAPI_DISPATCH_HOST_RESOURCE_CALL(
PpapiHostMsg_FileSystem_InitIsolatedFileSystem,
OnHostMsgInitIsolatedFileSystem)
+ PPAPI_DISPATCH_HOST_RESOURCE_CALL(
+ PpapiHostMsg_FileSystem_ReserveQuota,
+ OnHostMsgReserveQuota)
IPC_END_MESSAGE_MAP()
return PP_ERROR_FAILED;
}
@@ -140,12 +133,11 @@ void PepperFileSystemBrowserHost::OpenQuotaFile(
}
void PepperFileSystemBrowserHost::CloseQuotaFile(
- PepperFileIOHost* file_io_host) {
+ PepperFileIOHost* file_io_host,
+ int64_t max_written_offset) {
int32_t id = file_io_host->pp_resource();
- int64_t max_written_offset = 0;
FileMap::iterator it = files_.find(id);
if (it != files_.end()) {
- max_written_offset = file_io_host->max_written_offset();
files_.erase(it);
} else {
NOTREACHED();
@@ -160,25 +152,6 @@ void PepperFileSystemBrowserHost::CloseQuotaFile(
max_written_offset));
}
-int32_t PepperFileSystemBrowserHost::RequestQuota(
- int32_t amount,
- const RequestQuotaCallback& callback) {
- DCHECK(amount >= 0);
- if (!reserving_quota_ && reserved_quota_ >= amount) {
- reserved_quota_ -= amount;
- return amount;
- }
-
- // Queue up a pending quota request.
- pending_quota_requests_.push(QuotaRequest(amount, callback));
-
- // Reserve more quota if we haven't already.
- if (!reserving_quota_)
- ReserveQuota(amount);
-
- return PP_OK_COMPLETIONPENDING;
-}
-
int32_t PepperFileSystemBrowserHost::OnHostMsgOpen(
ppapi::host::HostMessageContext* context,
int64_t /* unused */) {
@@ -381,6 +354,33 @@ int32_t PepperFileSystemBrowserHost::OnHostMsgInitIsolatedFileSystem(
return PP_OK_COMPLETIONPENDING;
}
+int32_t PepperFileSystemBrowserHost::OnHostMsgReserveQuota(
+ ppapi::host::HostMessageContext* context,
+ int64_t amount,
+ const std::map<int32_t, int64_t>& max_written_offsets) {
+ DCHECK(ChecksQuota());
+ DCHECK(amount > 0);
+
+ if (reserving_quota_)
+ return PP_ERROR_INPROGRESS;
+ reserving_quota_ = true;
+
+ int64_t reservation_amount = std::max<int64_t>(kMinimumQuotaReservationSize,
+ amount);
+ file_system_context_->default_file_task_runner()->PostTask(
+ FROM_HERE,
+ base::Bind(&QuotaReservation::ReserveQuota,
+ quota_reservation_,
+ reservation_amount,
+ max_written_offsets,
+ base::Bind(&PepperFileSystemBrowserHost::GotReservedQuota,
+ weak_factory_.GetWeakPtr(),
+ context->MakeReplyMessageContext())));
+
+
+ return PP_OK_COMPLETIONPENDING;
+}
+
void PepperFileSystemBrowserHost::SendReplyForFileSystem(
ppapi::host::ReplyMessageContext reply_context,
int32_t pp_error) {
@@ -413,7 +413,7 @@ bool PepperFileSystemBrowserHost::ShouldCreateQuotaReservation() const {
if (!ppapi::FileSystemTypeHasQuota(type_))
return false;
- // For file system types with quota, ome origins have unlimited storage.
+ // For file system types with quota, some origins have unlimited storage.
quota::QuotaManagerProxy* quota_manager_proxy =
file_system_context_->quota_manager_proxy();
CHECK(quota_manager_proxy);
@@ -447,67 +447,18 @@ void PepperFileSystemBrowserHost::GotQuotaReservation(
callback.Run();
}
-void PepperFileSystemBrowserHost::ReserveQuota(int32_t amount) {
- DCHECK(!reserving_quota_);
- reserving_quota_ = true;
-
- // Get the max_written_offset for each open file.
- QuotaReservation::OffsetMap max_written_offsets;
- for (FileMap::iterator it = files_.begin(); it != files_.end(); ++ it) {
- max_written_offsets.insert(
- std::make_pair(it->first, it->second->max_written_offset()));
- }
-
- int64_t reservation_amount = std::max<int64_t>(kMinimumQuotaReservationSize,
- amount);
- file_system_context_->default_file_task_runner()->PostTask(
- FROM_HERE,
- base::Bind(&QuotaReservation::ReserveQuota,
- quota_reservation_,
- reservation_amount,
- max_written_offsets,
- base::Bind(&PepperFileSystemBrowserHost::GotReservedQuota,
- weak_factory_.GetWeakPtr())));
-}
-
void PepperFileSystemBrowserHost::GotReservedQuota(
+ ppapi::host::ReplyMessageContext reply_context,
int64_t amount,
const QuotaReservation::OffsetMap& max_written_offsets) {
DCHECK(reserving_quota_);
reserving_quota_ = false;
reserved_quota_ = amount;
- // Update open files with their new base sizes. This won't write over any
- // updates since the files are waiting for quota and can't write.
- for (FileMap::iterator it = files_.begin(); it != files_.end(); ++ it) {
- QuotaReservation::OffsetMap::const_iterator offset_it =
- max_written_offsets.find(it->first);
- if (offset_it != max_written_offsets.end())
- it->second->set_max_written_offset(offset_it->second);
- else
- NOTREACHED();
- }
-
- DCHECK(!pending_quota_requests_.empty());
- // If we can't grant the first request after refreshing reserved_quota_, then
- // fail all pending quota requests to avoid an infinite refresh/fail loop.
- bool fail_all = reserved_quota_ < pending_quota_requests_.front().amount;
- while (!pending_quota_requests_.empty()) {
- QuotaRequest& request = pending_quota_requests_.front();
- if (fail_all) {
- request.callback.Run(0);
- pending_quota_requests_.pop();
- } else if (reserved_quota_ >= request.amount) {
- reserved_quota_ -= request.amount;
- request.callback.Run(request.amount);
- pending_quota_requests_.pop();
- } else {
- // Refresh the quota reservation for the first pending request that we
- // can't satisfy.
- ReserveQuota(request.amount);
- break;
- }
- }
+ reply_context.params.set_result(PP_OK);
+ host()->SendReply(
+ reply_context,
+ PpapiPluginMsg_FileSystem_ReserveQuotaReply(amount, max_written_offsets));
}
std::string PepperFileSystemBrowserHost::GetPluginMimeType() const {
diff --git a/content/browser/renderer_host/pepper/pepper_file_system_browser_host.h b/content/browser/renderer_host/pepper/pepper_file_system_browser_host.h
index bd47a43..6a90fae 100644
--- a/content/browser/renderer_host/pepper/pepper_file_system_browser_host.h
+++ b/content/browser/renderer_host/pepper/pepper_file_system_browser_host.h
@@ -72,26 +72,12 @@ class CONTENT_EXPORT PepperFileSystemBrowserHost
const OpenQuotaFileCallback& callback);
// Closes the file. This must be called after OpenQuotaFile and before the
// PepperFileIOHost is destroyed.
- void CloseQuotaFile(PepperFileIOHost* file_io_host);
- // Requests the given amount of quota. Returns the amount requested or
- // PP_OK_COMPLETIONPENDING, in which case the amount granted is returned in
- // the callback. Requests can't partially succeed so the amount granted is
- // either 0 or the amount of the request. Requesting an amount of 0 will
- // return immediately with a 0 result.
- typedef base::Callback<void(int32_t)> RequestQuotaCallback;
- int32_t RequestQuota(int32_t amount,
- const RequestQuotaCallback& callback);
+ void CloseQuotaFile(PepperFileIOHost* file_io_host,
+ int64_t max_written_offset);
+
private:
friend class PepperFileSystemBrowserHostTest;
- struct QuotaRequest {
- QuotaRequest(int32_t amount, const RequestQuotaCallback& callback);
- ~QuotaRequest();
-
- int32_t amount;
- RequestQuotaCallback callback;
- };
-
void OpenExistingFileSystem(
const base::Closure& callback,
scoped_refptr<fileapi::FileSystemContext> file_system_context);
@@ -118,12 +104,17 @@ class CONTENT_EXPORT PepperFileSystemBrowserHost
const std::string& fsid,
base::PlatformFileError error);
- int32_t OnHostMsgOpen(ppapi::host::HostMessageContext* context,
- int64_t expected_size);
+ int32_t OnHostMsgOpen(
+ ppapi::host::HostMessageContext* context,
+ int64_t expected_size);
int32_t OnHostMsgInitIsolatedFileSystem(
ppapi::host::HostMessageContext* context,
const std::string& fsid,
PP_IsolatedFileSystemType_Private type);
+ int32_t OnHostMsgReserveQuota(
+ ppapi::host::HostMessageContext* context,
+ int64_t amount,
+ const std::map<int32_t, int64_t>& max_written_offsets);
void SendReplyForFileSystem(
ppapi::host::ReplyMessageContext reply_context,
@@ -142,9 +133,14 @@ class CONTENT_EXPORT PepperFileSystemBrowserHost
const base::Closure& callback,
scoped_refptr<QuotaReservation> quota_reservation);
- void ReserveQuota(int32_t amount);
- void GotReservedQuota(int64_t amount,
- const QuotaReservation::OffsetMap& max_written_offsets);
+ void GotReservedQuota(
+ ppapi::host::ReplyMessageContext reply_context,
+ int64_t amount,
+ const std::map<int32_t, int64_t>& max_written_offsets);
+ void DidOpenQuotaFile(
+ PP_Resource file_io_resource,
+ const OpenQuotaFileCallback& callback,
+ int64_t max_written_offset);
std::string GetPluginMimeType() const;
@@ -167,7 +163,6 @@ class CONTENT_EXPORT PepperFileSystemBrowserHost
// destroyed.
typedef std::map<int32_t, PepperFileIOHost*> FileMap;
FileMap files_;
- std::queue<QuotaRequest> pending_quota_requests_;
int64_t reserved_quota_;
bool reserving_quota_;
// Access only on the FileSystemContext's default_file_task_runner().
diff --git a/content/browser/renderer_host/pepper/quota_reservation.cc b/content/browser/renderer_host/pepper/quota_reservation.cc
index a512f10..d928f23 100644
--- a/content/browser/renderer_host/pepper/quota_reservation.cc
+++ b/content/browser/renderer_host/pepper/quota_reservation.cc
@@ -78,7 +78,7 @@ void QuotaReservation::ReserveQuota(
int64_t amount,
const OffsetMap& max_written_offsets,
const ReserveQuotaCallback& callback) {
- for (FileMap::iterator it = files_.begin(); it != files_.end(); ++ it) {
+ for (FileMap::iterator it = files_.begin(); it != files_.end(); ++it) {
OffsetMap::const_iterator offset_it = max_written_offsets.find(it->first);
if (offset_it != max_written_offsets.end())
it->second->UpdateMaxWrittenOffset(offset_it->second);
diff --git a/ppapi/ppapi_tests.gypi b/ppapi/ppapi_tests.gypi
index 0b4570a..9a4dd52 100644
--- a/ppapi/ppapi_tests.gypi
+++ b/ppapi/ppapi_tests.gypi
@@ -174,6 +174,7 @@
'proxy/interface_list_unittest.cc',
'proxy/mock_resource.cc',
'proxy/mock_resource.h',
+ 'proxy/nacl_message_scanner_unittest.cc',
'proxy/pdf_resource_unittest.cc',
'proxy/plugin_dispatcher_unittest.cc',
'proxy/plugin_resource_tracker_unittest.cc',
@@ -207,7 +208,7 @@
}],
],
# TODO(jschuh): crbug.com/167187 fix size_t to int truncations.
- 'msvs_disabled_warnings': [ 4267, ],
+ 'msvs_disabled_warnings': [ 4267, ],
},
{
'target_name': 'ppapi_example_skeleton',
diff --git a/ppapi/proxy/file_io_resource.cc b/ppapi/proxy/file_io_resource.cc
index ad07187..e86c321 100644
--- a/ppapi/proxy/file_io_resource.cc
+++ b/ppapi/proxy/file_io_resource.cc
@@ -80,9 +80,39 @@ int32_t FileIOResource::ReadOp::DoWork() {
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() {
+ // We can't just call WritePlatformFile in append mode, since NaCl doesn't
+ // implement fcntl, causing the function to call pwrite, which is incorrect.
+ 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),
+ check_quota_(false),
called_close_(false) {
SendCreate(BROWSER, PpapiHostMsg_FileIO_Create());
}
@@ -113,6 +143,7 @@ int32_t FileIOResource::Open(PP_Resource file_ref,
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) {
@@ -239,22 +270,42 @@ 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 (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;
- // TODO(brettw) it would be nice to use a shared memory buffer for large
- // writes rather than having to copy to a string (which will involve a number
- // of extra copies to serialize over IPC).
- bytes_to_write = std::min(bytes_to_write, kMaxReadWriteSize);
- Call<PpapiPluginMsg_FileIO_GeneralReply>(BROWSER,
- PpapiHostMsg_FileIO_Write(offset, std::string(buffer, bytes_to_write)),
- base::Bind(&FileIOResource::OnPluginMsgGeneralComplete, this,
- callback));
-
state_manager_.SetPendingOperation(FileIOStateManager::OPERATION_WRITE);
- return PP_OK_COMPLETIONPENDING;
+
+ if (check_quota_) {
+ int64_t actual_offset =
+ (open_flags_ & PP_FILEOPENFLAG_APPEND) ? max_written_offset_ : offset;
+
+ uint64_t max_offset = actual_offset + bytes_to_write;
+ if (max_offset > static_cast<uint64_t>(std::numeric_limits<int64_t>::max()))
+ return PP_ERROR_FAILED; // amount calculation would overflow.
+ int64_t 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);
+ max_written_offset_ = max_offset;
+ }
+ }
+ return WriteValidated(offset, buffer, bytes_to_write, callback);
}
int32_t FileIOResource::SetLength(int64_t length,
@@ -263,13 +314,30 @@ int32_t FileIOResource::SetLength(int64_t length,
FileIOStateManager::OPERATION_EXCLUSIVE, true);
if (rv != PP_OK)
return rv;
+ if (length < 0)
+ return PP_ERROR_FAILED;
- Call<PpapiPluginMsg_FileIO_GeneralReply>(BROWSER,
- PpapiHostMsg_FileIO_SetLength(length),
- base::Bind(&FileIOResource::OnPluginMsgGeneralComplete, this,
- callback));
+ 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;
}
@@ -288,15 +356,29 @@ int32_t FileIOResource::Flush(scoped_refptr<TrackedCallback> callback) {
return PP_OK_COMPLETIONPENDING;
}
+int64_t FileIOResource::GetMaxWrittenOffset() const {
+ return max_written_offset_;
+}
+
+void FileIOResource::SetMaxWrittenOffset(int64_t max_written_offset) {
+ max_written_offset_ = max_written_offset;
+}
+
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());
+ Post(BROWSER, PpapiHostMsg_FileIO_Close(max_written_offset_));
}
int32_t FileIOResource::RequestOSFileHandle(
@@ -380,6 +462,57 @@ int32_t FileIOResource::ReadValidated(int64_t offset,
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;
+ {
+ // Release the proxy lock while making a potentially slow file call.
+ 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;
+ }
+
+ // For the non-blocking case, post a task to the file thread.
+ 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));
+
+ max_written_offset_ = length;
+}
+
int32_t FileIOResource::OnQueryComplete(scoped_refptr<QueryOp> query_op,
PP_FileInfo* info,
int32_t result) {
@@ -416,6 +549,49 @@ int32_t FileIOResource::OnReadComplete(scoped_refptr<ReadOp> read_op,
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;
+ }
+ max_written_offset_ += granted;
+ 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;
+ }
+
+ 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);
+ // |result| is the return value of WritePlatformFile; -1 indicates failure.
+ if (result < 0)
+ result = PP_ERROR_FAILED;
+
+ state_manager_.SetOperationFinished();
+ return result;
+}
+
void FileIOResource::OnPluginMsgGeneralComplete(
scoped_refptr<TrackedCallback> callback,
const ResourceMessageReplyParams& params) {
@@ -431,20 +607,31 @@ void FileIOResource::OnPluginMsgGeneralComplete(
void FileIOResource::OnPluginMsgOpenFileComplete(
scoped_refptr<TrackedCallback> callback,
- const ResourceMessageReplyParams& params) {
+ const ResourceMessageReplyParams& params,
+ PP_Resource quota_file_system,
+ int64_t max_written_offset) {
DCHECK(state_manager_.get_pending_operation() ==
FileIOStateManager::OPERATION_EXCLUSIVE);
// Release the FileRef resource.
file_ref_ = NULL;
- if (params.result() == PP_OK)
+ int32_t result = params.result();
+ if (result == PP_OK) {
state_manager_.SetOpenSucceed();
- int32_t result = params.result();
- IPC::PlatformFileForTransit transit_file;
- if ((result == PP_OK) && params.TakeFileHandleAtIndex(0, &transit_file)) {
- file_handle_ = new FileHandleHolder(
- IPC::PlatformFileForTransitToPlatformFile(transit_file));
+ 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));
+ }
}
// End this operation now, so the user's callback can execute another FileIO
// operation, assuming there are no other pending operations.
diff --git a/ppapi/proxy/file_io_resource.h b/ppapi/proxy/file_io_resource.h
index bfdf24f..23df0e6 100644
--- a/ppapi/proxy/file_io_resource.h
+++ b/ppapi/proxy/file_io_resource.h
@@ -57,6 +57,8 @@ class PPAPI_PROXY_EXPORT FileIOResource
scoped_refptr<TrackedCallback> callback) OVERRIDE;
virtual int32_t SetLength(int64_t length,
scoped_refptr<TrackedCallback> callback) OVERRIDE;
+ virtual int64_t GetMaxWrittenOffset() const OVERRIDE;
+ virtual void SetMaxWrittenOffset(int64_t max_written_offset) OVERRIDE;
virtual int32_t Flush(scoped_refptr<TrackedCallback> callback) OVERRIDE;
virtual void Close() OVERRIDE;
virtual int32_t RequestOSFileHandle(
@@ -139,10 +141,49 @@ class PPAPI_PROXY_EXPORT FileIOResource
scoped_ptr<char[]> buffer_;
};
+ // Class to perform file write operations across multiple threads.
+ class WriteOp : public base::RefCountedThreadSafe<WriteOp> {
+ public:
+ WriteOp(scoped_refptr<FileHandleHolder> file_handle,
+ int64_t offset,
+ const char* buffer,
+ int32_t bytes_to_write,
+ bool append);
+
+ // Writes the file. Called on the file thread (non-blocking) or the plugin
+ // thread (blocking). This should not be called when we hold the proxy lock.
+ int32_t DoWork();
+
+ private:
+ friend class base::RefCountedThreadSafe<WriteOp>;
+ ~WriteOp();
+
+ scoped_refptr<FileHandleHolder> file_handle_;
+ int64_t offset_;
+ const char* buffer_;
+ int32_t bytes_to_write_;
+ bool append_;
+ };
+
+ void OnRequestWriteQuotaComplete(int64_t offset,
+ const char* buffer,
+ int32_t bytes_to_write,
+ scoped_refptr<TrackedCallback> callback,
+ int64_t granted);
+ void OnRequestSetLengthQuotaComplete(int64_t length,
+ scoped_refptr<TrackedCallback> callback,
+ int64_t granted);
+
int32_t ReadValidated(int64_t offset,
int32_t bytes_to_read,
const PP_ArrayOutput& array_output,
scoped_refptr<TrackedCallback> callback);
+ int32_t WriteValidated(int64_t offset,
+ const char* buffer,
+ int32_t bytes_to_write,
+ scoped_refptr<TrackedCallback> callback);
+ void SetLengthValidated(int64_t length,
+ scoped_refptr<TrackedCallback> callback);
// Completion tasks for file operations that are done in the plugin.
int32_t OnQueryComplete(scoped_refptr<QueryOp> query_op,
@@ -151,12 +192,16 @@ class PPAPI_PROXY_EXPORT FileIOResource
int32_t OnReadComplete(scoped_refptr<ReadOp> read_op,
PP_ArrayOutput array_output,
int32_t result);
+ int32_t OnWriteComplete(scoped_refptr<WriteOp> write_op,
+ int32_t result);
// Reply message handlers for operations that are done in the host.
void OnPluginMsgGeneralComplete(scoped_refptr<TrackedCallback> callback,
const ResourceMessageReplyParams& params);
void OnPluginMsgOpenFileComplete(scoped_refptr<TrackedCallback> callback,
- const ResourceMessageReplyParams& params);
+ const ResourceMessageReplyParams& params,
+ PP_Resource quota_file_system,
+ int64_t max_written_offset);
void OnPluginMsgRequestOSFileHandleComplete(
scoped_refptr<TrackedCallback> callback,
PP_FileHandle* output_handle,
@@ -165,11 +210,15 @@ class PPAPI_PROXY_EXPORT FileIOResource
scoped_refptr<FileHandleHolder> file_handle_;
PP_FileSystemType file_system_type_;
scoped_refptr<Resource> file_system_resource_;
- bool called_close_;
FileIOStateManager state_manager_;
scoped_refptr<Resource> file_ref_;
+ int32_t open_flags_;
+ int64_t max_written_offset_;
+ bool check_quota_;
+ bool called_close_;
+
DISALLOW_COPY_AND_ASSIGN(FileIOResource);
};
diff --git a/ppapi/proxy/file_system_resource.cc b/ppapi/proxy/file_system_resource.cc
index df8c390..8061189 100644
--- a/ppapi/proxy/file_system_resource.cc
+++ b/ppapi/proxy/file_system_resource.cc
@@ -9,12 +9,26 @@
#include "ppapi/c/pp_errors.h"
#include "ppapi/proxy/ppapi_messages.h"
#include "ppapi/shared_impl/tracked_callback.h"
+#include "ppapi/thunk/enter.h"
+#include "ppapi/thunk/ppb_file_io_api.h"
+using ppapi::thunk::EnterResourceNoLock;
+using ppapi::thunk::PPB_FileIO_API;
using ppapi::thunk::PPB_FileSystem_API;
namespace ppapi {
namespace proxy {
+FileSystemResource::QuotaRequest::QuotaRequest(
+ int64_t amount_arg,
+ const RequestQuotaCallback& callback_arg)
+ : amount(amount_arg),
+ callback(callback_arg) {
+}
+
+FileSystemResource::QuotaRequest::~QuotaRequest() {
+}
+
FileSystemResource::FileSystemResource(Connection connection,
PP_Instance instance,
PP_FileSystemType type)
@@ -22,7 +36,9 @@ FileSystemResource::FileSystemResource(Connection connection,
type_(type),
called_open_(false),
callback_count_(0),
- callback_result_(PP_OK) {
+ callback_result_(PP_OK),
+ reserved_quota_(0),
+ reserving_quota_(false) {
DCHECK(type_ != PP_FILESYSTEMTYPE_INVALID);
SendCreate(RENDERER, PpapiHostMsg_FileSystem_Create(type_));
SendCreate(BROWSER, PpapiHostMsg_FileSystem_Create(type_));
@@ -37,7 +53,9 @@ FileSystemResource::FileSystemResource(Connection connection,
type_(type),
called_open_(true),
callback_count_(0),
- callback_result_(PP_OK) {
+ callback_result_(PP_OK),
+ reserved_quota_(0),
+ reserving_quota_(false) {
DCHECK(type_ != PP_FILESYSTEMTYPE_INVALID);
AttachToPendingHost(RENDERER, pending_renderer_id);
AttachToPendingHost(BROWSER, pending_browser_id);
@@ -74,6 +92,39 @@ PP_FileSystemType FileSystemResource::GetType() {
return type_;
}
+void FileSystemResource::OpenQuotaFile(PP_Resource file_io) {
+ DCHECK(max_written_offsets_.find(file_io) == max_written_offsets_.end());
+ EnterResourceNoLock<PPB_FileIO_API> enter(file_io, true);
+ DCHECK(!enter.failed());
+ PPB_FileIO_API* file_io_api = enter.object();
+ max_written_offsets_[file_io] = file_io_api->GetMaxWrittenOffset();
+}
+
+void FileSystemResource::CloseQuotaFile(PP_Resource file_io) {
+ OffsetMap::iterator it = max_written_offsets_.find(file_io);
+ DCHECK(it != max_written_offsets_.end());
+ max_written_offsets_.erase(it);
+}
+
+int64_t FileSystemResource::RequestQuota(
+ int64_t amount,
+ const RequestQuotaCallback& callback) {
+ DCHECK(amount >= 0);
+ if (!reserving_quota_ && reserved_quota_ >= amount) {
+ reserved_quota_ -= amount;
+ return amount;
+ }
+
+ // Queue up a pending quota request.
+ pending_quota_requests_.push(QuotaRequest(amount, callback));
+
+ // Reserve more quota if we haven't already.
+ if (!reserving_quota_)
+ ReserveQuota(amount);
+
+ return PP_OK_COMPLETIONPENDING;
+}
+
int32_t FileSystemResource::InitIsolatedFileSystem(
const std::string& fsid,
PP_IsolatedFileSystemType_Private type,
@@ -122,5 +173,59 @@ void FileSystemResource::InitIsolatedFileSystemComplete(
callback.Run(callback_result_);
}
+void FileSystemResource::ReserveQuota(int64_t amount) {
+ DCHECK(!reserving_quota_);
+ reserving_quota_ = true;
+ for (OffsetMap::iterator it = max_written_offsets_.begin();
+ it != max_written_offsets_.end(); ++it) {
+ EnterResourceNoLock<PPB_FileIO_API> enter(it->first, true);
+ DCHECK(!enter.failed());
+ PPB_FileIO_API* file_io_api = enter.object();
+ it->second = file_io_api->GetMaxWrittenOffset();
+ }
+ Call<PpapiPluginMsg_FileSystem_ReserveQuotaReply>(BROWSER,
+ PpapiHostMsg_FileSystem_ReserveQuota(amount, max_written_offsets_),
+ base::Bind(&FileSystemResource::ReserveQuotaComplete,
+ this));
+}
+
+void FileSystemResource::ReserveQuotaComplete(
+ const ResourceMessageReplyParams& params,
+ int64_t amount,
+ const OffsetMap& max_written_offsets) {
+ DCHECK(reserving_quota_);
+ reserving_quota_ = false;
+ reserved_quota_ = amount;
+
+ for (OffsetMap::const_iterator it = max_written_offsets.begin();
+ it != max_written_offsets.end(); ++it) {
+ EnterResourceNoLock<PPB_FileIO_API> enter(it->first, true);
+ DCHECK(!enter.failed());
+ PPB_FileIO_API* file_io_api = enter.object();
+ file_io_api->SetMaxWrittenOffset(it->second);
+ }
+
+ DCHECK(!pending_quota_requests_.empty());
+ // If we can't grant the first request after refreshing reserved_quota_, then
+ // fail all pending quota requests to avoid an infinite refresh/fail loop.
+ bool fail_all = reserved_quota_ < pending_quota_requests_.front().amount;
+ while (!pending_quota_requests_.empty()) {
+ QuotaRequest& request = pending_quota_requests_.front();
+ if (fail_all) {
+ request.callback.Run(0);
+ pending_quota_requests_.pop();
+ } else if (reserved_quota_ >= request.amount) {
+ reserved_quota_ -= request.amount;
+ request.callback.Run(request.amount);
+ pending_quota_requests_.pop();
+ } else {
+ // Refresh the quota reservation for the first pending request that we
+ // can't satisfy.
+ ReserveQuota(request.amount);
+ break;
+ }
+ }
+}
+
} // namespace proxy
} // namespace ppapi
diff --git a/ppapi/proxy/file_system_resource.h b/ppapi/proxy/file_system_resource.h
index 9029583..e3e22b1 100644
--- a/ppapi/proxy/file_system_resource.h
+++ b/ppapi/proxy/file_system_resource.h
@@ -5,10 +5,13 @@
#ifndef PPAPI_PROXY_FILE_SYSTEM_RESOURCE_H_
#define PPAPI_PROXY_FILE_SYSTEM_RESOURCE_H_
+#include <map>
+#include <queue>
#include <string>
#include "base/memory/ref_counted.h"
#include "ppapi/c/pp_file_info.h"
+#include "ppapi/c/pp_resource.h"
#include "ppapi/c/private/ppb_isolated_file_system_private.h"
#include "ppapi/proxy/connection.h"
#include "ppapi/proxy/plugin_resource.h"
@@ -48,11 +51,25 @@ class PPAPI_PROXY_EXPORT FileSystemResource
virtual int32_t Open(int64_t expected_size,
scoped_refptr<TrackedCallback> callback) OVERRIDE;
virtual PP_FileSystemType GetType() OVERRIDE;
+ virtual void OpenQuotaFile(PP_Resource file_io) OVERRIDE;
+ virtual void CloseQuotaFile(PP_Resource file_io) OVERRIDE;
+ typedef base::Callback<void(int64_t)> RequestQuotaCallback;
+ virtual int64_t RequestQuota(int64_t amount,
+ const RequestQuotaCallback& callback) OVERRIDE;
int32_t InitIsolatedFileSystem(const std::string& fsid,
PP_IsolatedFileSystemType_Private type,
const base::Callback<void(int32_t)>& callback);
private:
+ struct QuotaRequest {
+ QuotaRequest(int64_t amount,
+ const RequestQuotaCallback& callback);
+ ~QuotaRequest();
+
+ int64_t amount;
+ RequestQuotaCallback callback;
+ };
+
// Called when the host has responded to our open request.
void OpenComplete(scoped_refptr<TrackedCallback> callback,
const ResourceMessageReplyParams& params);
@@ -62,11 +79,22 @@ class PPAPI_PROXY_EXPORT FileSystemResource
const base::Callback<void(int32_t)>& callback,
const ResourceMessageReplyParams& params);
+ void ReserveQuota(int64_t amount);
+ typedef std::map<int32_t, int64_t> OffsetMap;
+ void ReserveQuotaComplete(const ResourceMessageReplyParams& params,
+ int64_t amount,
+ const OffsetMap& max_written_offsets);
+
PP_FileSystemType type_;
bool called_open_;
uint32_t callback_count_;
int32_t callback_result_;
+ OffsetMap max_written_offsets_;
+ std::queue<QuotaRequest> pending_quota_requests_;
+ int64_t reserved_quota_;
+ bool reserving_quota_;
+
DISALLOW_COPY_AND_ASSIGN(FileSystemResource);
};
diff --git a/ppapi/proxy/nacl_message_scanner.cc b/ppapi/proxy/nacl_message_scanner.cc
index 602b18e..3c2b45f8 100644
--- a/ppapi/proxy/nacl_message_scanner.cc
+++ b/ppapi/proxy/nacl_message_scanner.cc
@@ -19,12 +19,16 @@ namespace IPC {
class Message;
}
+using ppapi::proxy::ResourceMessageReplyParams;
+using ppapi::proxy::SerializedHandle;
+using ppapi::proxy::SerializedVar;
+
namespace {
-typedef std::vector<ppapi::proxy::SerializedHandle> Handles;
+typedef std::vector<SerializedHandle> Handles;
struct ScanningResults {
- ScanningResults() : handle_index(0) {}
+ ScanningResults() : handle_index(0), pp_resource(0) {}
// Vector to hold handles found in the message.
Handles handles;
@@ -36,12 +40,18 @@ struct ScanningResults {
// may set this to NULL when it can determine that there are no parameters
// that need conversion. (See the ResourceMessageReplyParams overload.)
scoped_ptr<IPC::Message> new_msg;
+ // Resource id for resource messages. Save this when scanning resource replies
+ // so when we audit the nested message, we know which resource it is for.
+ PP_Resource pp_resource;
+ // Callback to receive the nested message in a resource message or reply.
+ base::Callback<void(PP_Resource, const IPC::Message&, SerializedHandle*)>
+ nested_msg_callback;
};
void WriteHandle(int handle_index,
- const ppapi::proxy::SerializedHandle& handle,
+ const SerializedHandle& handle,
IPC::Message* msg) {
- ppapi::proxy::SerializedHandle::WriteHeader(handle.header(), msg);
+ SerializedHandle::WriteHeader(handle.header(), msg);
// Now write the handle itself in POSIX style.
msg->WriteBool(true); // valid == true
@@ -52,8 +62,7 @@ void WriteHandle(int handle_index,
// handling. See ScanTuple for how these get used.
// Overload to match SerializedHandle.
-void ScanParam(const ppapi::proxy::SerializedHandle& handle,
- ScanningResults* results) {
+void ScanParam(const SerializedHandle& handle, ScanningResults* results) {
results->handles.push_back(handle);
if (results->new_msg)
WriteHandle(results->handle_index++, handle, results->new_msg.get());
@@ -61,14 +70,13 @@ void ScanParam(const ppapi::proxy::SerializedHandle& handle,
void HandleWriter(int* handle_index,
IPC::Message* m,
- const ppapi::proxy::SerializedHandle& handle) {
+ const SerializedHandle& handle) {
WriteHandle((*handle_index)++, handle, m);
}
// Overload to match SerializedVar, which can contain handles.
-void ScanParam(const ppapi::proxy::SerializedVar& var,
- ScanningResults* results) {
- std::vector<ppapi::proxy::SerializedHandle*> var_handles = var.GetHandles();
+void ScanParam(const SerializedVar& var, ScanningResults* results) {
+ std::vector<SerializedHandle*> var_handles = var.GetHandles();
// Copy any handles and then rewrite the message.
for (size_t i = 0; i < var_handles.size(); ++i)
results->handles.push_back(*var_handles[i]);
@@ -82,8 +90,9 @@ void ScanParam(const ppapi::proxy::SerializedVar& var,
// NOTE: We only intercept handles from host->NaCl. The only kind of
// ResourceMessageParams that travels this direction is
// ResourceMessageReplyParams, so that's the only one we need to handle.
-void ScanParam(const ppapi::proxy::ResourceMessageReplyParams& params,
+void ScanParam(const ResourceMessageReplyParams& params,
ScanningResults* results) {
+ results->pp_resource = params.pp_resource();
// If the resource reply params don't contain handles, NULL the new message
// pointer to cancel further rewriting.
// NOTE: This works because only handles currently need rewriting, and we
@@ -112,8 +121,21 @@ void ScanParam(const ppapi::proxy::ResourceMessageReplyParams& params,
params.ConsumeHandles();
}
-// Overload to match all other types. If we need to rewrite the message,
-// write the parameter.
+// Overload to match nested messages. If we need to rewrite the message, write
+// the parameter.
+void ScanParam(const IPC::Message& param, ScanningResults* results) {
+ if (results->pp_resource && !results->nested_msg_callback.is_null()) {
+ SerializedHandle* handle = NULL;
+ if (results->handles.size() == 1)
+ handle = &results->handles[0];
+ results->nested_msg_callback.Run(results->pp_resource, param, handle);
+ }
+ if (results->new_msg)
+ IPC::WriteParam(results->new_msg.get(), param);
+}
+
+// Overload to match all other types. If we need to rewrite the message, write
+// the parameter.
template <class T>
void ScanParam(const T& param, ScanningResults* results) {
if (results->new_msg)
@@ -211,9 +233,58 @@ namespace proxy {
class SerializedHandle;
+NaClMessageScanner::FileSystem::FileSystem()
+ : reserved_quota_(0) {
+}
+
+NaClMessageScanner::FileSystem::~FileSystem() {
+}
+
+bool NaClMessageScanner::FileSystem::UpdateReservedQuota(int64_t delta) {
+ base::AutoLock lock(lock_);
+ if (std::numeric_limits<int64_t>::max() - reserved_quota_ < delta)
+ return false; // reserved_quota_ + delta would overflow.
+ if (reserved_quota_ + delta < 0)
+ return false;
+ reserved_quota_ += delta;
+ return true;
+}
+
+NaClMessageScanner::FileIO::FileIO(FileSystem* file_system,
+ int64_t max_written_offset)
+ : file_system_(file_system),
+ max_written_offset_(max_written_offset) {
+}
+
+NaClMessageScanner::FileIO::~FileIO() {
+}
+
+void NaClMessageScanner::FileIO::SetMaxWrittenOffset(
+ int64_t max_written_offset) {
+ base::AutoLock lock(lock_);
+ max_written_offset_ = max_written_offset;
+}
+
+bool NaClMessageScanner::FileIO::Grow(int64_t amount) {
+ base::AutoLock lock(lock_);
+ DCHECK(amount > 0);
+ if (!file_system_->UpdateReservedQuota(-amount))
+ return false;
+ max_written_offset_ += amount;
+ return true;
+}
+
NaClMessageScanner::NaClMessageScanner() {
}
+NaClMessageScanner::~NaClMessageScanner() {
+ for (FileSystemMap::iterator it = file_systems_.begin();
+ it != file_systems_.end(); ++it)
+ delete it->second;
+ for (FileIOMap::iterator it = files_.begin(); it != files_.end(); ++it)
+ delete it->second;
+}
+
// Windows IPC differs from POSIX in that native handles are serialized in the
// message body, rather than passed in a separate FileDescriptorSet. Therefore,
// on Windows, any message containing handles must be rewritten in the POSIX
@@ -239,12 +310,14 @@ bool NaClMessageScanner::ScanMessage(
(msg.type() == PpapiMsg_CreateNaClChannel::ID);
#endif
-
// We can't always tell from the message ID if rewriting is needed. Therefore,
// scan any message types that might contain a handle. If we later determine
// that there are no handles, we can cancel the rewriting by clearing the
// results.new_msg pointer.
ScanningResults results;
+ results.nested_msg_callback =
+ base::Bind(&NaClMessageScanner::AuditNestedMessage,
+ base::Unretained(this));
switch (msg.type()) {
CASE_FOR_MESSAGE(PpapiMsg_CreateNaClChannel)
CASE_FOR_MESSAGE(PpapiMsg_PPBAudio_NotifyAudioStreamCreated)
@@ -290,8 +363,113 @@ void NaClMessageScanner::ScanUntrustedMessage(
scoped_ptr<IPC::Message>* new_msg_ptr) {
if (untrusted_msg.is_sync())
RegisterSyncMessageForReply(untrusted_msg);
- // TODO(bbudge) Add message auditing for FileSystem and FileIO resources when
- // we implement Write on the plugin side of the proxy. See crbug.com/194304.
+
+ // Audit FileIO and FileSystem messages to ensure that the plugin doesn't
+ // exceed its file quota. If we find the message is malformed, just pass it
+ // through - we only care about well formed messages to the host.
+ if (untrusted_msg.type() == PpapiHostMsg_ResourceCall::ID) {
+ ResourceMessageCallParams params;
+ IPC::Message nested_msg;
+ if (!UnpackMessage<PpapiHostMsg_ResourceCall>(
+ untrusted_msg, &params, &nested_msg))
+ return;
+
+ switch (nested_msg.type()) {
+ case PpapiHostMsg_FileIO_Close::ID: {
+ FileIOMap::iterator it = files_.find(params.pp_resource());
+ if (it == files_.end())
+ return;
+ // Audit FileIO Close messages to make sure the plugin reports an
+ // accurate file size.
+ int64_t max_written_offset = 0;
+ if (!UnpackMessage<PpapiHostMsg_FileIO_Close>(
+ nested_msg, &max_written_offset))
+ return;
+
+ int64_t trusted_max_written_offset = it->second->max_written_offset();
+ delete it->second;
+ files_.erase(it);
+ // If the plugin is under-reporting, rewrite the message with the
+ // trusted value.
+ if (trusted_max_written_offset > max_written_offset) {
+ new_msg_ptr->reset(
+ new PpapiHostMsg_ResourceCall(
+ params,
+ PpapiHostMsg_FileIO_Close(trusted_max_written_offset)));
+ }
+ }
+ case PpapiHostMsg_FileIO_SetLength::ID: {
+ FileIOMap::iterator it = files_.find(params.pp_resource());
+ if (it == files_.end())
+ return;
+ // Audit FileIO SetLength messages to make sure the plugin is within
+ // the current quota reservation. In addition, deduct the file size
+ // increase from the quota reservation.
+ int64_t length = 0;
+ if (!UnpackMessage<PpapiHostMsg_FileIO_SetLength>(
+ nested_msg, &length))
+ return;
+
+ // Calculate file size increase, taking care to avoid overflows.
+ if (length < 0)
+ return;
+ int64_t trusted_max_written_offset = it->second->max_written_offset();
+ int64_t increase = length - trusted_max_written_offset;
+ if (increase <= 0)
+ return;
+ if (!it->second->Grow(increase)) {
+ new_msg_ptr->reset(
+ new PpapiHostMsg_ResourceCall(
+ params,
+ PpapiHostMsg_FileIO_SetLength(-1)));
+ }
+ break;
+ }
+ case PpapiHostMsg_FileSystem_ReserveQuota::ID: {
+ // Audit FileSystem ReserveQuota messages to make sure the plugin
+ // reports accurate file sizes.
+ int64_t amount = 0;
+ FileOffsetMap max_written_offsets;
+ if (!UnpackMessage<PpapiHostMsg_FileSystem_ReserveQuota>(
+ nested_msg, &amount, &max_written_offsets))
+ return;
+
+ bool audit_failed = false;
+ for (FileOffsetMap::iterator it = max_written_offsets.begin();
+ it != max_written_offsets.end(); ++it) {
+ FileIOMap::iterator file_it = files_.find(it->first);
+ if (file_it == files_.end())
+ continue;
+ int64_t trusted_max_written_offset =
+ file_it->second->max_written_offset();
+ if (trusted_max_written_offset > it->second) {
+ audit_failed = true;
+ it->second = trusted_max_written_offset;
+ }
+ }
+ if (audit_failed) {
+ new_msg_ptr->reset(
+ new PpapiHostMsg_ResourceCall(
+ params,
+ PpapiHostMsg_FileSystem_ReserveQuota(
+ amount, max_written_offsets)));
+ }
+ break;
+ }
+ case PpapiHostMsg_ResourceDestroyed::ID: {
+ // Audit resource destroyed messages to release FileSystems.
+ PP_Resource resource;
+ if (!UnpackMessage<PpapiHostMsg_ResourceDestroyed>(
+ nested_msg, &resource))
+ return;
+ FileSystemMap::iterator fs_it = file_systems_.find(resource);
+ if (fs_it != file_systems_.end()) {
+ delete fs_it->second;
+ file_systems_.erase(fs_it);
+ }
+ }
+ }
+ }
}
void NaClMessageScanner::RegisterSyncMessageForReply(const IPC::Message& msg) {
@@ -301,5 +479,64 @@ void NaClMessageScanner::RegisterSyncMessageForReply(const IPC::Message& msg) {
pending_sync_msgs_[msg_id] = msg.type();
}
+NaClMessageScanner::FileIO* NaClMessageScanner::GetFile(
+ PP_Resource file_io) {
+ FileIOMap::iterator it = files_.find(file_io);
+ DCHECK(it != files_.end());
+ return it->second;
+}
+
+void NaClMessageScanner::AuditNestedMessage(PP_Resource resource,
+ const IPC::Message& msg,
+ SerializedHandle* handle) {
+ switch (msg.type()) {
+ case PpapiPluginMsg_FileIO_OpenReply::ID: {
+ // A file that requires quota checking was opened.
+ PP_Resource quota_file_system;
+ int64_t max_written_offset = 0;
+ if (ppapi::UnpackMessage<PpapiPluginMsg_FileIO_OpenReply>(
+ msg, &quota_file_system, &max_written_offset)) {
+ if (quota_file_system) {
+ // Look up the FileSystem by inserting a new one. If it was already
+ // present, get the existing one, otherwise construct it.
+ FileSystem* file_system = NULL;
+ std::pair<FileSystemMap::iterator, bool> insert_result =
+ file_systems_.insert(std::make_pair(quota_file_system,
+ file_system));
+ if (insert_result.second)
+ insert_result.first->second = new FileSystem();
+ file_system = insert_result.first->second;
+ // Create the FileIO.
+ DCHECK(files_.find(resource) == files_.end());
+ files_.insert(std::make_pair(
+ resource,
+ new FileIO(file_system, max_written_offset)));
+ }
+ }
+ break;
+ }
+ case PpapiPluginMsg_FileSystem_ReserveQuotaReply::ID: {
+ // The amount of reserved quota for a FileSystem was refreshed.
+ int64_t amount = 0;
+ FileOffsetMap max_written_offsets;
+ if (ppapi::UnpackMessage<PpapiPluginMsg_FileSystem_ReserveQuotaReply>(
+ msg, &amount, &max_written_offsets)) {
+ FileSystemMap::iterator it = file_systems_.find(resource);
+ DCHECK(it != file_systems_.end());
+ it->second->UpdateReservedQuota(amount);
+
+ FileOffsetMap::const_iterator offset_it = max_written_offsets.begin();
+ for (; offset_it != max_written_offsets.end(); ++offset_it) {
+ FileIOMap::iterator fio_it = files_.find(offset_it->first);
+ DCHECK(fio_it != files_.end());
+ if (fio_it != files_.end())
+ fio_it->second->SetMaxWrittenOffset(offset_it->second);
+ }
+ }
+ break;
+ }
+ }
+}
+
} // namespace proxy
} // namespace ppapi
diff --git a/ppapi/proxy/nacl_message_scanner.h b/ppapi/proxy/nacl_message_scanner.h
index 8704160..d1360b7 100644
--- a/ppapi/proxy/nacl_message_scanner.h
+++ b/ppapi/proxy/nacl_message_scanner.h
@@ -10,6 +10,8 @@
#include "base/basictypes.h"
#include "base/memory/scoped_ptr.h"
+#include "base/synchronization/lock.h"
+#include "ppapi/c/pp_resource.h"
#include "ppapi/proxy/ppapi_proxy_export.h"
namespace IPC {
@@ -24,6 +26,7 @@ class SerializedHandle;
class PPAPI_PROXY_EXPORT NaClMessageScanner {
public:
NaClMessageScanner();
+ ~NaClMessageScanner();
// Scans the message for items that require special handling. Copies any
// SerializedHandles in the message into |handles| and if the message must be
@@ -44,8 +47,63 @@ class PPAPI_PROXY_EXPORT NaClMessageScanner {
void ScanUntrustedMessage(const IPC::Message& untrusted_msg,
scoped_ptr<IPC::Message>* new_msg_ptr);
+ // FileSystem information for quota auditing.
+ class PPAPI_PROXY_EXPORT FileSystem {
+ public:
+ FileSystem();
+ ~FileSystem();
+
+ int64_t reserved_quota() const { return reserved_quota_; }
+
+ // Adds amount to reserved quota. Returns true if reserved quota >= 0.
+ bool UpdateReservedQuota(int64_t delta);
+
+ private:
+ base::Lock lock_;
+ // This is the remaining amount of quota reserved for the file system.
+ // Acquire the lock to modify this field, since it may be used on multiple
+ // threads.
+ int64_t reserved_quota_;
+
+ DISALLOW_COPY_AND_ASSIGN(FileSystem);
+ };
+
+ // FileIO information for quota auditing.
+ class PPAPI_PROXY_EXPORT FileIO {
+ public:
+ FileIO(FileSystem* file_system, int64_t max_written_offset);
+ ~FileIO();
+
+ int64_t max_written_offset() { return max_written_offset_; }
+
+ void SetMaxWrittenOffset(int64_t max_written_offset);
+
+ // Grows file by the given amount. Returns true on success.
+ bool Grow(int64_t amount);
+
+ private:
+ base::Lock lock_;
+
+ // The file system that contains this file.
+ FileSystem* file_system_;
+
+ // The maximum written offset. This is initialized by NaClMessageScanner
+ // when the file is opened and modified by a NaClDescQuotaInterface when the
+ // plugin writes to greater maximum offsets.
+ int64_t max_written_offset_;
+
+ DISALLOW_COPY_AND_ASSIGN(FileIO);
+ };
+
+ FileIO* GetFile(PP_Resource file_io);
+
private:
+ friend class NaClMessageScannerTest;
+
void RegisterSyncMessageForReply(const IPC::Message& msg);
+ void AuditNestedMessage(PP_Resource resource,
+ const IPC::Message& msg,
+ SerializedHandle* handle);
// When we send a synchronous message (from untrusted to trusted), we store
// its type here, so that later we can associate the reply with its type
@@ -53,6 +111,14 @@ class PPAPI_PROXY_EXPORT NaClMessageScanner {
typedef std::map<int, uint32> PendingSyncMsgMap;
PendingSyncMsgMap pending_sync_msgs_;
+ // We intercept FileSystem and FileIO messages to maintain information about
+ // file systems and open files. This is used by NaClQuotaDescs to calculate
+ // quota consumption and check it against the reserved amount.
+ typedef std::map<int32_t, FileSystem*> FileSystemMap;
+ FileSystemMap file_systems_;
+ typedef std::map<int32_t, FileIO*> FileIOMap;
+ FileIOMap files_;
+
DISALLOW_COPY_AND_ASSIGN(NaClMessageScanner);
};
diff --git a/ppapi/proxy/nacl_message_scanner_unittest.cc b/ppapi/proxy/nacl_message_scanner_unittest.cc
new file mode 100644
index 0000000..e0516ff
--- /dev/null
+++ b/ppapi/proxy/nacl_message_scanner_unittest.cc
@@ -0,0 +1,293 @@
+// Copyright 2013 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 "ipc/ipc_message.h"
+#include "ppapi/proxy/nacl_message_scanner.h"
+#include "ppapi/proxy/ppapi_messages.h"
+#include "ppapi/proxy/ppapi_proxy_test.h"
+#include "ppapi/proxy/serialized_handle.h"
+#include "ppapi/shared_impl/host_resource.h"
+
+namespace ppapi {
+namespace proxy {
+
+namespace {
+const PP_Resource kInvalidResource = 0;
+const PP_Resource kFileSystem = 1;
+const PP_Resource kFileIO = 2;
+const int64_t kQuotaReservationAmount = 100;
+}
+
+class NaClMessageScannerTest : public PluginProxyTest {
+ public:
+ NaClMessageScannerTest() {}
+
+ uint32 FindPendingSyncMessage(
+ const NaClMessageScanner& scanner,
+ const IPC::Message& msg) {
+ int msg_id = IPC::SyncMessage::GetMessageId(msg);
+ std::map<int, uint32>::const_iterator it =
+ scanner.pending_sync_msgs_.find(msg_id);
+ // O can signal that no message was found.
+ return (it != scanner.pending_sync_msgs_.end()) ? it->second : 0;
+ }
+
+ NaClMessageScanner::FileSystem* FindFileSystem(
+ const NaClMessageScanner& scanner,
+ PP_Resource file_system) {
+ NaClMessageScanner::FileSystemMap::const_iterator it =
+ scanner.file_systems_.find(file_system);
+ return (it != scanner.file_systems_.end()) ? it->second : NULL;
+ }
+
+ NaClMessageScanner::FileIO* FindFileIO(
+ const NaClMessageScanner& scanner,
+ PP_Resource file_io) {
+ NaClMessageScanner::FileIOMap::const_iterator it =
+ scanner.files_.find(file_io);
+ return (it != scanner.files_.end()) ? it->second : NULL;
+ }
+
+ void OpenQuotaFile(NaClMessageScanner* scanner,
+ PP_Resource file_io,
+ PP_Resource file_system) {
+ std::vector<SerializedHandle> unused_handles;
+ ResourceMessageReplyParams fio_reply_params(file_io, 0);
+ scoped_ptr<IPC::Message> new_msg_ptr;
+ scanner->ScanMessage(
+ PpapiPluginMsg_ResourceReply(
+ fio_reply_params,
+ PpapiPluginMsg_FileIO_OpenReply(file_system, 0)),
+ &unused_handles,
+ &new_msg_ptr);
+ EXPECT_FALSE(new_msg_ptr);
+ }
+};
+
+TEST_F(NaClMessageScannerTest, SyncMessageAndReply) {
+ NaClMessageScanner test;
+ ppapi::proxy::SerializedHandle handle(
+ ppapi::proxy::SerializedHandle::SHARED_MEMORY);
+ IPC::Message msg =
+ PpapiHostMsg_PPBGraphics3D_GetTransferBuffer(
+ ppapi::API_ID_PPB_GRAPHICS_3D,
+ HostResource(),
+ 0, // id
+ &handle);
+ scoped_ptr<IPC::Message> new_msg_ptr;
+ EXPECT_NE(msg.type(), FindPendingSyncMessage(test, msg));
+ test.ScanUntrustedMessage(msg, &new_msg_ptr);
+ EXPECT_FALSE(new_msg_ptr);
+ EXPECT_EQ(msg.type(), FindPendingSyncMessage(test, msg));
+
+ // TODO(bbudge) Figure out how to put together a sync reply message.
+}
+
+TEST_F(NaClMessageScannerTest, FileOpenClose) {
+ NaClMessageScanner test;
+ std::vector<SerializedHandle> unused_handles;
+ ResourceMessageCallParams fio_call_params(kFileIO, 0);
+ ResourceMessageCallParams fs_call_params(kFileSystem, 0);
+ ResourceMessageReplyParams fio_reply_params(kFileIO, 0);
+ ResourceMessageReplyParams fs_reply_params(kFileSystem, 0);
+ scoped_ptr<IPC::Message> new_msg_ptr;
+
+ EXPECT_EQ(NULL, FindFileSystem(test, kFileSystem));
+ EXPECT_EQ(NULL, FindFileIO(test, kFileIO));
+
+ // Open a file, not in a quota file system.
+ test.ScanMessage(
+ PpapiPluginMsg_ResourceReply(
+ fio_reply_params,
+ PpapiPluginMsg_FileIO_OpenReply(kInvalidResource, 0)),
+ &unused_handles,
+ &new_msg_ptr);
+ EXPECT_FALSE(new_msg_ptr);
+ EXPECT_FALSE(FindFileSystem(test, kFileSystem));
+ EXPECT_FALSE(FindFileIO(test, kFileIO));
+
+ // Open a file in a quota file system; info objects for it and its file system
+ // should be created.
+ OpenQuotaFile(&test, kFileIO, kFileSystem);
+ NaClMessageScanner::FileSystem* fs = FindFileSystem(test, kFileSystem);
+ NaClMessageScanner::FileIO* fio = FindFileIO(test, kFileIO);
+ EXPECT_TRUE(fs);
+ EXPECT_EQ(0, fs->reserved_quota());
+ EXPECT_TRUE(fio);
+ EXPECT_EQ(0, fio->max_written_offset());
+
+ const int64_t kNewFileSize = 10;
+ fio->SetMaxWrittenOffset(kNewFileSize);
+
+ // We should not be able to under-report max_written_offset when closing.
+ test.ScanUntrustedMessage(
+ PpapiHostMsg_ResourceCall(
+ fio_call_params,
+ PpapiHostMsg_FileIO_Close(0)),
+ &new_msg_ptr);
+ EXPECT_TRUE(new_msg_ptr);
+ ResourceMessageCallParams call_params;
+ IPC::Message nested_msg;
+ int64_t max_written_offset = 0;
+ EXPECT_TRUE(UnpackMessage<PpapiHostMsg_ResourceCall>(
+ *new_msg_ptr, &call_params, &nested_msg) &&
+ UnpackMessage<PpapiHostMsg_FileIO_Close>(
+ nested_msg, &max_written_offset));
+ new_msg_ptr.reset();
+ EXPECT_EQ(kNewFileSize, max_written_offset);
+ EXPECT_FALSE(FindFileIO(test, kFileIO));
+
+ // Reopen the file.
+ OpenQuotaFile(&test, kFileIO, kFileSystem);
+ fio = FindFileIO(test, kFileIO);
+ fio->SetMaxWrittenOffset(kNewFileSize);
+
+ // Close with correct max_written_offset.
+ test.ScanUntrustedMessage(
+ PpapiHostMsg_ResourceCall(
+ fio_call_params,
+ PpapiHostMsg_FileIO_Close(kNewFileSize)),
+ &new_msg_ptr);
+ EXPECT_FALSE(new_msg_ptr);
+ EXPECT_FALSE(FindFileIO(test, kFileIO));
+
+ // Destroy file system.
+ test.ScanUntrustedMessage(
+ PpapiHostMsg_ResourceCall(
+ fs_call_params,
+ PpapiHostMsg_ResourceDestroyed(kFileSystem)),
+ &new_msg_ptr);
+ EXPECT_FALSE(FindFileSystem(test, kFileSystem));
+}
+
+TEST_F(NaClMessageScannerTest, QuotaAuditing) {
+ NaClMessageScanner test;
+ std::vector<SerializedHandle> unused_handles;
+ ResourceMessageCallParams fio_call_params(kFileIO, 0);
+ ResourceMessageCallParams fs_call_params(kFileSystem, 0);
+ ResourceMessageReplyParams fio_reply_params(kFileIO, 0);
+ ResourceMessageReplyParams fs_reply_params(kFileSystem, 0);
+ scoped_ptr<IPC::Message> new_msg_ptr;
+
+ OpenQuotaFile(&test, kFileIO, kFileSystem);
+ NaClMessageScanner::FileSystem* fs = FindFileSystem(test, kFileSystem);
+ NaClMessageScanner::FileIO* fio = FindFileIO(test, kFileIO);
+ EXPECT_TRUE(fs);
+ EXPECT_EQ(0, fs->reserved_quota());
+ EXPECT_TRUE(fio);
+ EXPECT_EQ(0, fio->max_written_offset());
+
+ // Without reserving quota, we should not be able to grow the file.
+ EXPECT_FALSE(fio->Grow(1));
+ EXPECT_EQ(0, fs->reserved_quota());
+ EXPECT_EQ(0, fio->max_written_offset());
+
+ // Receive reserved quota, and updated file sizes.
+ const int64_t kNewFileSize = 10;
+ FileOffsetMap offset_map;
+ offset_map.insert(std::make_pair(kFileIO, kNewFileSize));
+ test.ScanMessage(
+ PpapiPluginMsg_ResourceReply(
+ fs_reply_params,
+ PpapiPluginMsg_FileSystem_ReserveQuotaReply(
+ kQuotaReservationAmount,
+ offset_map)),
+ &unused_handles,
+ &new_msg_ptr);
+ EXPECT_FALSE(new_msg_ptr);
+ EXPECT_EQ(kQuotaReservationAmount, fs->reserved_quota());
+ EXPECT_EQ(kNewFileSize, fio->max_written_offset());
+
+ // We should be able to grow the file within quota.
+ EXPECT_TRUE(fio->Grow(1));
+ EXPECT_EQ(kQuotaReservationAmount - 1, fs->reserved_quota());
+ EXPECT_EQ(kNewFileSize + 1, fio->max_written_offset());
+
+ // We should not be able to grow the file over quota.
+ EXPECT_FALSE(fio->Grow(kQuotaReservationAmount));
+ EXPECT_EQ(kQuotaReservationAmount - 1, fs->reserved_quota());
+ EXPECT_EQ(kNewFileSize + 1, fio->max_written_offset());
+
+ // Plugin should not under-report max written offsets when reserving quota.
+ offset_map[kFileIO] = 0; // should be kNewFileSize + 1.
+ test.ScanUntrustedMessage(
+ PpapiHostMsg_ResourceCall(
+ fio_call_params,
+ PpapiHostMsg_FileSystem_ReserveQuota(
+ kQuotaReservationAmount,
+ offset_map)),
+ &new_msg_ptr);
+ EXPECT_TRUE(new_msg_ptr);
+ ResourceMessageCallParams call_params;
+ IPC::Message nested_msg;
+ int64_t amount = 0;
+ FileOffsetMap new_offset_map;
+ EXPECT_TRUE(UnpackMessage<PpapiHostMsg_ResourceCall>(
+ *new_msg_ptr, &call_params, &nested_msg) &&
+ UnpackMessage<PpapiHostMsg_FileSystem_ReserveQuota>(
+ nested_msg, &amount, &new_offset_map));
+ new_msg_ptr.reset();
+ EXPECT_EQ(kQuotaReservationAmount, amount);
+ EXPECT_EQ(kNewFileSize + 1, new_offset_map[kFileIO]);
+}
+
+TEST_F(NaClMessageScannerTest, SetLength) {
+ NaClMessageScanner test;
+ std::vector<SerializedHandle> unused_handles;
+ ResourceMessageCallParams fio_call_params(kFileIO, 0);
+ ResourceMessageCallParams fs_call_params(kFileSystem, 0);
+ ResourceMessageReplyParams fio_reply_params(kFileIO, 0);
+ ResourceMessageReplyParams fs_reply_params(kFileSystem, 0);
+ scoped_ptr<IPC::Message> new_msg_ptr;
+
+ OpenQuotaFile(&test, kFileIO, kFileSystem);
+ NaClMessageScanner::FileSystem* fs = FindFileSystem(test, kFileSystem);
+ NaClMessageScanner::FileIO* fio = FindFileIO(test, kFileIO);
+
+ // Receive reserved quota, and updated file sizes.
+ const int64_t kNewFileSize = 10;
+ FileOffsetMap offset_map;
+ offset_map.insert(std::make_pair(kFileIO, 0));
+ test.ScanMessage(
+ PpapiPluginMsg_ResourceReply(
+ fs_reply_params,
+ PpapiPluginMsg_FileSystem_ReserveQuotaReply(
+ kQuotaReservationAmount,
+ offset_map)),
+ &unused_handles,
+ &new_msg_ptr);
+
+ // We should be able to SetLength within quota.
+ test.ScanUntrustedMessage(
+ PpapiHostMsg_ResourceCall(
+ fio_call_params,
+ PpapiHostMsg_FileIO_SetLength(kNewFileSize)),
+ &new_msg_ptr);
+ EXPECT_FALSE(new_msg_ptr);
+ EXPECT_EQ(kQuotaReservationAmount - kNewFileSize, fs->reserved_quota());
+ EXPECT_EQ(kNewFileSize, fio->max_written_offset());
+
+ // We shouldn't be able to SetLength beyond quota. The message should be
+ // rewritten to fail with length == -1.
+ test.ScanUntrustedMessage(
+ PpapiHostMsg_ResourceCall(
+ fio_call_params,
+ PpapiHostMsg_FileIO_SetLength(kQuotaReservationAmount + 1)),
+ &new_msg_ptr);
+ EXPECT_TRUE(new_msg_ptr);
+ ResourceMessageCallParams call_params;
+ IPC::Message nested_msg;
+ int64_t length = 0;
+ EXPECT_TRUE(UnpackMessage<PpapiHostMsg_ResourceCall>(
+ *new_msg_ptr, &call_params, &nested_msg) &&
+ UnpackMessage<PpapiHostMsg_FileIO_SetLength>(
+ nested_msg, &length));
+ new_msg_ptr.reset();
+ EXPECT_EQ(-1, length);
+ EXPECT_EQ(kQuotaReservationAmount - kNewFileSize, fs->reserved_quota());
+ EXPECT_EQ(kNewFileSize, fio->max_written_offset());
+}
+
+} // namespace proxy
+} // namespace ppapi
diff --git a/ppapi/proxy/ppapi_messages.h b/ppapi/proxy/ppapi_messages.h
index ca4ea36..b2fa2af 100644
--- a/ppapi/proxy/ppapi_messages.h
+++ b/ppapi/proxy/ppapi_messages.h
@@ -3,6 +3,7 @@
// found in the LICENSE file.
// Multiply-included message header, no traditional include guard.
+#include <map>
#include <string>
#include <vector>
@@ -1224,14 +1225,14 @@ IPC_MESSAGE_CONTROL0(PpapiHostMsg_FileIO_Create)
IPC_MESSAGE_CONTROL2(PpapiHostMsg_FileIO_Open,
PP_Resource /* file_ref_resource */,
int32_t /* open_flags */)
-IPC_MESSAGE_CONTROL0(PpapiPluginMsg_FileIO_OpenReply)
-IPC_MESSAGE_CONTROL0(PpapiHostMsg_FileIO_Close)
+IPC_MESSAGE_CONTROL2(PpapiPluginMsg_FileIO_OpenReply,
+ PP_Resource /* quota_file_system */,
+ int64_t /* max_written_offset */)
+IPC_MESSAGE_CONTROL1(PpapiHostMsg_FileIO_Close,
+ int64_t /* max_written_offset */)
IPC_MESSAGE_CONTROL2(PpapiHostMsg_FileIO_Touch,
PP_Time /* last_access_time */,
PP_Time /* last_modified_time */)
-IPC_MESSAGE_CONTROL2(PpapiHostMsg_FileIO_Write,
- int64_t /* offset */,
- std::string /* data */)
IPC_MESSAGE_CONTROL1(PpapiHostMsg_FileIO_SetLength,
int64_t /* length */)
IPC_MESSAGE_CONTROL0(PpapiHostMsg_FileIO_Flush)
@@ -1317,6 +1318,15 @@ IPC_MESSAGE_CONTROL2(PpapiHostMsg_FileSystem_CreateFromRenderer,
// linked to the existing resource host given in the ResourceVar.
IPC_MESSAGE_CONTROL1(PpapiPluginMsg_FileSystem_CreateFromPendingHost,
PP_FileSystemType /* file_system_type */)
+// IPC_MESSAGE macros choke on extra , in the std::map, when expanding. We need
+// to typedef it to avoid that.
+typedef std::map<int32_t, int64_t> FileOffsetMap;
+IPC_MESSAGE_CONTROL2(PpapiHostMsg_FileSystem_ReserveQuota,
+ int64_t /* amount */,
+ FileOffsetMap /* max_written_offsets */)
+IPC_MESSAGE_CONTROL2(PpapiPluginMsg_FileSystem_ReserveQuotaReply,
+ int64_t /* amount */,
+ FileOffsetMap /* max_written_offsets */)
// Flash DRM ------------------------------------------------------------------
IPC_MESSAGE_CONTROL0(PpapiHostMsg_FlashDRM_Create)
diff --git a/ppapi/thunk/ppb_file_io_api.h b/ppapi/thunk/ppb_file_io_api.h
index b794f1c..830b140 100644
--- a/ppapi/thunk/ppb_file_io_api.h
+++ b/ppapi/thunk/ppb_file_io_api.h
@@ -42,6 +42,8 @@ class PPAPI_THUNK_EXPORT PPB_FileIO_API {
scoped_refptr<TrackedCallback> callback) = 0;
virtual int32_t SetLength(int64_t length,
scoped_refptr<TrackedCallback> callback) = 0;
+ virtual int64_t GetMaxWrittenOffset() const = 0;
+ virtual void SetMaxWrittenOffset(int64_t max_written_offset) = 0;
virtual int32_t Flush(scoped_refptr<TrackedCallback> callback) = 0;
virtual void Close() = 0;
diff --git a/ppapi/thunk/ppb_file_system_api.h b/ppapi/thunk/ppb_file_system_api.h
index f9524dc..0351bde 100644
--- a/ppapi/thunk/ppb_file_system_api.h
+++ b/ppapi/thunk/ppb_file_system_api.h
@@ -5,6 +5,7 @@
#ifndef PPAPI_THUNK_PPB_FILE_SYSTEM_API_H_
#define PPAPI_THUNK_PPB_FILE_SYSTEM_API_H_
+#include "base/callback_forward.h"
#include "base/memory/ref_counted.h"
#include "ppapi/c/ppb_file_system.h"
@@ -21,6 +22,11 @@ class PPB_FileSystem_API {
virtual int32_t Open(int64_t expected_size,
scoped_refptr<TrackedCallback> callback) = 0;
virtual PP_FileSystemType GetType() = 0;
+ virtual void OpenQuotaFile(PP_Resource file_io) = 0;
+ virtual void CloseQuotaFile(PP_Resource file_io) = 0;
+ typedef base::Callback<void(int64_t)> RequestQuotaCallback;
+ virtual int64_t RequestQuota(int64_t amount,
+ const RequestQuotaCallback& callback) = 0;
};
} // namespace thunk