summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorbbudge@chromium.org <bbudge@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2013-12-28 02:17:38 +0000
committerbbudge@chromium.org <bbudge@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2013-12-28 02:17:38 +0000
commit2e9903c857ee69a3f8e4d1dfea961ff6fafe7ba3 (patch)
treee9ecf46fce22eed8383fd03c4339e840ddf7746c
parent0ab279ae9dc3c5f24a00c2365fc3e3507c92bce4 (diff)
downloadchromium_src-2e9903c857ee69a3f8e4d1dfea961ff6fafe7ba3.zip
chromium_src-2e9903c857ee69a3f8e4d1dfea961ff6fafe7ba3.tar.gz
chromium_src-2e9903c857ee69a3f8e4d1dfea961ff6fafe7ba3.tar.bz2
Do PPB_FileIO Write on the plugin side.
This eliminates IPC for FileIO.Write, instead using the plugin's file descriptor. Performs the file op on the plugin's thread if blocking, otherwise on the file thread. FileIOResources know their maximum written offset after they are opened. They can thus calculate whether a Write or SetLength operation will extend this max offset. If so, they call FileSystemResource::RequestQuota. This can return synchronously or asynchronously, and either returns the requested amount or 0 in case it can't be satisfied. FileSystemResource will request a quota reservation from the host, queuing up unsatisfied requests. The quota reservation will be at least 1MB with this CL. The main point of a quota reservation is to reduce the number of times we query the quota system. Both the FileSystemResource and the host maintain a collection of open files that are subject to quota. These are kept in sync. Maps that take PP_Resource to resource or host are used to ensure good performance and make it easier to keep the resource and host collections synced. SetLength uses the plugin side machinery to request quota. When its request is granted, it calls over to the host side as before. This is because of OS X sandbox restrictions. For trusted plugins, I assume that this plugin / host checking is sufficient. In particular, SetLength is performed in the browser process without checking the length against quota restrictions. We're essentially assuming trusted plugins don't cheat. For untrusted plugins, this approach isn't sufficient. NaClMessageScanner audits the FIleSystem and FileIO message traffic to maintain a parallel accounting of quota reservation and current file sizes. In addition, we wrap the native file handle in a NaClDescQuota for files that need quota checking. This NaClDescQuota uses NaClMessageScanner information to determine whether or not to allow a Write. We must check at the descriptor level since the untrusted plugin may bypass our proxy code and use the file handle directly to Write. We must also fail any attempt to call ftruncate directly. BUG=194304 Review URL: https://codereview.chromium.org/100703004 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@242659 0039d316-1c4b-4281-b951-d872f2087c98
-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