| // 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 "content/browser/renderer_host/pepper/pepper_file_io_host.h" | 
 |  | 
 | #include <utility> | 
 |  | 
 | #include "base/bind.h" | 
 | #include "base/callback.h" | 
 | #include "base/files/file_util_proxy.h" | 
 | #include "base/memory/weak_ptr.h" | 
 | #include "base/task_scheduler/post_task.h" | 
 | #include "content/browser/renderer_host/pepper/pepper_file_ref_host.h" | 
 | #include "content/browser/renderer_host/pepper/pepper_file_system_browser_host.h" | 
 | #include "content/browser/renderer_host/pepper/pepper_security_helper.h" | 
 | #include "content/common/fileapi/file_system_messages.h" | 
 | #include "content/common/view_messages.h" | 
 | #include "content/public/browser/browser_thread.h" | 
 | #include "content/public/browser/content_browser_client.h" | 
 | #include "content/public/browser/render_process_host.h" | 
 | #include "content/public/browser/storage_partition.h" | 
 | #include "content/public/common/content_client.h" | 
 | #include "ipc/ipc_platform_file.h" | 
 | #include "ppapi/c/pp_errors.h" | 
 | #include "ppapi/c/ppb_file_io.h" | 
 | #include "ppapi/host/dispatch_host_message.h" | 
 | #include "ppapi/host/ppapi_host.h" | 
 | #include "ppapi/proxy/ppapi_messages.h" | 
 | #include "ppapi/shared_impl/file_system_util.h" | 
 | #include "ppapi/shared_impl/file_type_conversion.h" | 
 | #include "ppapi/shared_impl/time_conversion.h" | 
 | #include "storage/browser/fileapi/file_observers.h" | 
 | #include "storage/browser/fileapi/file_system_context.h" | 
 | #include "storage/browser/fileapi/file_system_operation_runner.h" | 
 | #include "storage/browser/fileapi/task_runner_bound_observer_list.h" | 
 | #include "storage/common/fileapi/file_system_util.h" | 
 |  | 
 | namespace content { | 
 |  | 
 | using ppapi::FileIOStateManager; | 
 | using ppapi::PPTimeToTime; | 
 |  | 
 | namespace { | 
 |  | 
 | PepperFileIOHost::UIThreadStuff GetUIThreadStuffForInternalFileSystems( | 
 |     int render_process_id) { | 
 |   PepperFileIOHost::UIThreadStuff stuff; | 
 |   DCHECK_CURRENTLY_ON(BrowserThread::UI); | 
 |   RenderProcessHost* host = RenderProcessHost::FromID(render_process_id); | 
 |   if (host) { | 
 |     stuff.resolved_render_process_id = base::GetProcId(host->GetHandle()); | 
 |     StoragePartition* storage_partition = host->GetStoragePartition(); | 
 |     if (storage_partition) | 
 |       stuff.file_system_context = storage_partition->GetFileSystemContext(); | 
 |   } | 
 |   return stuff; | 
 | } | 
 |  | 
 | base::ProcessId GetResolvedRenderProcessId(int render_process_id) { | 
 |   DCHECK_CURRENTLY_ON(BrowserThread::UI); | 
 |   RenderProcessHost* host = RenderProcessHost::FromID(render_process_id); | 
 |   if (!host) | 
 |     return base::kNullProcessId; | 
 |   return base::GetProcId(host->GetHandle()); | 
 | } | 
 |  | 
 | bool GetPluginAllowedToCallRequestOSFileHandle(int render_process_id, | 
 |                                                const GURL& document_url) { | 
 |   DCHECK_CURRENTLY_ON(BrowserThread::UI); | 
 |   ContentBrowserClient* client = GetContentClient()->browser(); | 
 |   RenderProcessHost* host = RenderProcessHost::FromID(render_process_id); | 
 |   if (!host) | 
 |     return false; | 
 |   return client->IsPluginAllowedToCallRequestOSFileHandle( | 
 |       host->GetBrowserContext(), document_url); | 
 | } | 
 |  | 
 | bool FileOpenForWrite(int32_t open_flags) { | 
 |   return (open_flags & (PP_FILEOPENFLAG_WRITE | PP_FILEOPENFLAG_APPEND)) != 0; | 
 | } | 
 |  | 
 | void FileCloser(base::File auto_close) { | 
 | } | 
 |  | 
 | void DidCloseFile(base::OnceClosure on_close_callback) { | 
 |   if (!on_close_callback.is_null()) | 
 |     std::move(on_close_callback).Run(); | 
 | } | 
 |  | 
 | void DidOpenFile(base::WeakPtr<PepperFileIOHost> file_host, | 
 |                  scoped_refptr<base::SequencedTaskRunner> task_runner, | 
 |                  storage::FileSystemOperation::OpenFileCallback callback, | 
 |                  base::File file, | 
 |                  base::OnceClosure on_close_callback) { | 
 |   if (file_host) { | 
 |     std::move(callback).Run(std::move(file), std::move(on_close_callback)); | 
 |   } else { | 
 |     task_runner->PostTaskAndReply( | 
 |         FROM_HERE, base::BindOnce(&FileCloser, std::move(file)), | 
 |         base::BindOnce(&DidCloseFile, std::move(on_close_callback))); | 
 |   } | 
 | } | 
 |  | 
 | }  // namespace | 
 |  | 
 | PepperFileIOHost::PepperFileIOHost(BrowserPpapiHostImpl* host, | 
 |                                    PP_Instance instance, | 
 |                                    PP_Resource resource) | 
 |     : ResourceHost(host->GetPpapiHost(), instance, resource), | 
 |       browser_ppapi_host_(host), | 
 |       task_runner_(base::CreateSequencedTaskRunnerWithTraits( | 
 |           {base::MayBlock(), base::TaskPriority::USER_VISIBLE, | 
 |            base::TaskShutdownBehavior::BLOCK_SHUTDOWN})), | 
 |       file_(task_runner_.get()), | 
 |       open_flags_(0), | 
 |       file_system_type_(PP_FILESYSTEMTYPE_INVALID), | 
 |       max_written_offset_(0), | 
 |       check_quota_(false) { | 
 |   int unused; | 
 |   if (!host->GetRenderFrameIDsForInstance( | 
 |           instance, &render_process_id_, &unused)) { | 
 |     render_process_id_ = -1; | 
 |   } | 
 | } | 
 |  | 
 | PepperFileIOHost::~PepperFileIOHost() {} | 
 |  | 
 | int32_t PepperFileIOHost::OnResourceMessageReceived( | 
 |     const IPC::Message& msg, | 
 |     ppapi::host::HostMessageContext* context) { | 
 |   PPAPI_BEGIN_MESSAGE_MAP(PepperFileIOHost, msg) | 
 |     PPAPI_DISPATCH_HOST_RESOURCE_CALL(PpapiHostMsg_FileIO_Open, OnHostMsgOpen) | 
 |     PPAPI_DISPATCH_HOST_RESOURCE_CALL(PpapiHostMsg_FileIO_Touch, OnHostMsgTouch) | 
 |     PPAPI_DISPATCH_HOST_RESOURCE_CALL(PpapiHostMsg_FileIO_SetLength, | 
 |                                       OnHostMsgSetLength) | 
 |     PPAPI_DISPATCH_HOST_RESOURCE_CALL_0(PpapiHostMsg_FileIO_Flush, | 
 |                                         OnHostMsgFlush) | 
 |     PPAPI_DISPATCH_HOST_RESOURCE_CALL(PpapiHostMsg_FileIO_Close, OnHostMsgClose) | 
 |     PPAPI_DISPATCH_HOST_RESOURCE_CALL_0(PpapiHostMsg_FileIO_RequestOSFileHandle, | 
 |                                         OnHostMsgRequestOSFileHandle) | 
 |   PPAPI_END_MESSAGE_MAP() | 
 |   return PP_ERROR_FAILED; | 
 | } | 
 |  | 
 | PepperFileIOHost::UIThreadStuff::UIThreadStuff() { | 
 |   resolved_render_process_id = base::kNullProcessId; | 
 | } | 
 |  | 
 | PepperFileIOHost::UIThreadStuff::UIThreadStuff(const UIThreadStuff& other) = | 
 |     default; | 
 |  | 
 | PepperFileIOHost::UIThreadStuff::~UIThreadStuff() {} | 
 |  | 
 | int32_t PepperFileIOHost::OnHostMsgOpen( | 
 |     ppapi::host::HostMessageContext* context, | 
 |     PP_Resource file_ref_resource, | 
 |     int32_t open_flags) { | 
 |   int32_t rv = state_manager_.CheckOperationState( | 
 |       FileIOStateManager::OPERATION_EXCLUSIVE, false); | 
 |   if (rv != PP_OK) | 
 |     return rv; | 
 |  | 
 |   int platform_file_flags = 0; | 
 |   if (!ppapi::PepperFileOpenFlagsToPlatformFileFlags(open_flags, | 
 |                                                      &platform_file_flags)) | 
 |     return PP_ERROR_BADARGUMENT; | 
 |  | 
 |   ppapi::host::ResourceHost* resource_host = | 
 |       host()->GetResourceHost(file_ref_resource); | 
 |   if (!resource_host || !resource_host->IsFileRefHost()) | 
 |     return PP_ERROR_BADRESOURCE; | 
 |   PepperFileRefHost* file_ref_host = | 
 |       static_cast<PepperFileRefHost*>(resource_host); | 
 |   if (file_ref_host->GetFileSystemType() == PP_FILESYSTEMTYPE_INVALID) | 
 |     return PP_ERROR_FAILED; | 
 |  | 
 |   file_system_host_ = file_ref_host->GetFileSystemHost(); | 
 |  | 
 |   open_flags_ = open_flags; | 
 |   file_system_type_ = file_ref_host->GetFileSystemType(); | 
 |   file_system_url_ = file_ref_host->GetFileSystemURL(); | 
 |  | 
 |   // For external file systems, if there is a valid FileSystemURL, then treat | 
 |   // it like internal file systems and access it via the FileSystemURL. | 
 |   bool is_internal_type = (file_system_type_ != PP_FILESYSTEMTYPE_EXTERNAL) || | 
 |                           file_system_url_.is_valid(); | 
 |  | 
 |   if (is_internal_type) { | 
 |     if (!file_system_url_.is_valid()) | 
 |       return PP_ERROR_BADARGUMENT; | 
 |  | 
 |     // Not all external file systems are fully supported yet. | 
 |     // Whitelist the supported ones. | 
 |     if (file_system_url_.mount_type() == storage::kFileSystemTypeExternal) { | 
 |       switch (file_system_url_.type()) { | 
 |         case storage::kFileSystemTypeNativeMedia: | 
 |         case storage::kFileSystemTypeDeviceMedia: | 
 |           break; | 
 |         default: | 
 |           return PP_ERROR_NOACCESS; | 
 |       } | 
 |     } | 
 |     if (!CanOpenFileSystemURLWithPepperFlags( | 
 |             open_flags, render_process_id_, file_system_url_)) | 
 |       return PP_ERROR_NOACCESS; | 
 |     BrowserThread::PostTaskAndReplyWithResult( | 
 |         BrowserThread::UI, | 
 |         FROM_HERE, | 
 |         base::Bind(&GetUIThreadStuffForInternalFileSystems, render_process_id_), | 
 |         base::Bind(&PepperFileIOHost::GotUIThreadStuffForInternalFileSystems, | 
 |                    AsWeakPtr(), | 
 |                    context->MakeReplyMessageContext(), | 
 |                    platform_file_flags)); | 
 |   } else { | 
 |     base::FilePath path = file_ref_host->GetExternalFilePath(); | 
 |     if (!CanOpenWithPepperFlags(open_flags, render_process_id_, path)) | 
 |       return PP_ERROR_NOACCESS; | 
 |     BrowserThread::PostTaskAndReplyWithResult( | 
 |         BrowserThread::UI, | 
 |         FROM_HERE, | 
 |         base::Bind(&GetResolvedRenderProcessId, render_process_id_), | 
 |         base::Bind(&PepperFileIOHost::GotResolvedRenderProcessId, | 
 |                    AsWeakPtr(), | 
 |                    context->MakeReplyMessageContext(), | 
 |                    path, | 
 |                    platform_file_flags)); | 
 |   } | 
 |   state_manager_.SetPendingOperation(FileIOStateManager::OPERATION_EXCLUSIVE); | 
 |   return PP_OK_COMPLETIONPENDING; | 
 | } | 
 |  | 
 | void PepperFileIOHost::GotUIThreadStuffForInternalFileSystems( | 
 |     ppapi::host::ReplyMessageContext reply_context, | 
 |     int platform_file_flags, | 
 |     UIThreadStuff ui_thread_stuff) { | 
 |   DCHECK_CURRENTLY_ON(BrowserThread::IO); | 
 |   file_system_context_ = ui_thread_stuff.file_system_context; | 
 |   resolved_render_process_id_ = ui_thread_stuff.resolved_render_process_id; | 
 |   if (resolved_render_process_id_ == base::kNullProcessId || | 
 |       !file_system_context_.get()) { | 
 |     reply_context.params.set_result(PP_ERROR_FAILED); | 
 |     SendOpenErrorReply(reply_context); | 
 |     return; | 
 |   } | 
 |  | 
 |   if (!file_system_context_->GetFileSystemBackend(file_system_url_.type())) { | 
 |     reply_context.params.set_result(PP_ERROR_FAILED); | 
 |     SendOpenErrorReply(reply_context); | 
 |     return; | 
 |   } | 
 |  | 
 |   DCHECK(file_system_host_.get()); | 
 |   DCHECK(file_system_host_->GetFileSystemOperationRunner()); | 
 |  | 
 |   file_system_host_->GetFileSystemOperationRunner()->OpenFile( | 
 |       file_system_url_, platform_file_flags, | 
 |       base::Bind(&DidOpenFile, AsWeakPtr(), task_runner_, | 
 |                  base::Bind(&PepperFileIOHost::DidOpenInternalFile, AsWeakPtr(), | 
 |                             reply_context))); | 
 | } | 
 |  | 
 | void PepperFileIOHost::DidOpenInternalFile( | 
 |     ppapi::host::ReplyMessageContext reply_context, | 
 |     base::File file, | 
 |     base::OnceClosure on_close_callback) { | 
 |   if (file.IsValid()) { | 
 |     on_close_callback_ = std::move(on_close_callback); | 
 |  | 
 |     if (FileOpenForWrite(open_flags_) && file_system_host_->ChecksQuota()) { | 
 |       check_quota_ = true; | 
 |       file_system_host_->OpenQuotaFile( | 
 |           this, | 
 |           file_system_url_, | 
 |           base::Bind(&PepperFileIOHost::DidOpenQuotaFile, | 
 |                      AsWeakPtr(), | 
 |                      reply_context, | 
 |                      base::Passed(&file))); | 
 |       return; | 
 |     } | 
 |   } | 
 |  | 
 |   DCHECK(!file_.IsValid()); | 
 |   base::File::Error error = | 
 |       file.IsValid() ? base::File::FILE_OK : file.error_details(); | 
 |   file_.SetFile(std::move(file)); | 
 |   SendFileOpenReply(reply_context, error); | 
 | } | 
 |  | 
 | void PepperFileIOHost::GotResolvedRenderProcessId( | 
 |     ppapi::host::ReplyMessageContext reply_context, | 
 |     base::FilePath path, | 
 |     int file_flags, | 
 |     base::ProcessId resolved_render_process_id) { | 
 |   DCHECK_CURRENTLY_ON(BrowserThread::IO); | 
 |   resolved_render_process_id_ = resolved_render_process_id; | 
 |   file_.CreateOrOpen(path, file_flags, | 
 |                      base::BindOnce(&PepperFileIOHost::OnLocalFileOpened, | 
 |                                     AsWeakPtr(), reply_context, path)); | 
 | } | 
 |  | 
 | int32_t PepperFileIOHost::OnHostMsgTouch( | 
 |     ppapi::host::HostMessageContext* context, | 
 |     PP_Time last_access_time, | 
 |     PP_Time last_modified_time) { | 
 |   int32_t rv = state_manager_.CheckOperationState( | 
 |       FileIOStateManager::OPERATION_EXCLUSIVE, true); | 
 |   if (rv != PP_OK) | 
 |     return rv; | 
 |  | 
 |   if (!file_.SetTimes( | 
 |           PPTimeToTime(last_access_time), PPTimeToTime(last_modified_time), | 
 |           base::BindOnce(&PepperFileIOHost::ExecutePlatformGeneralCallback, | 
 |                          AsWeakPtr(), context->MakeReplyMessageContext()))) { | 
 |     return PP_ERROR_FAILED; | 
 |   } | 
 |  | 
 |   state_manager_.SetPendingOperation(FileIOStateManager::OPERATION_EXCLUSIVE); | 
 |   return PP_OK_COMPLETIONPENDING; | 
 | } | 
 |  | 
 | int32_t PepperFileIOHost::OnHostMsgSetLength( | 
 |     ppapi::host::HostMessageContext* context, | 
 |     int64_t length) { | 
 |   int32_t rv = state_manager_.CheckOperationState( | 
 |       FileIOStateManager::OPERATION_EXCLUSIVE, true); | 
 |   if (rv != PP_OK) | 
 |     return rv; | 
 |   if (length < 0) | 
 |     return PP_ERROR_BADARGUMENT; | 
 |  | 
 |   // Quota checks are performed on the plugin side, in order to use the same | 
 |   // quota reservation and request system as Write. | 
 |  | 
 |   if (!file_.SetLength( | 
 |           length, | 
 |           base::BindOnce(&PepperFileIOHost::ExecutePlatformGeneralCallback, | 
 |                          AsWeakPtr(), context->MakeReplyMessageContext()))) { | 
 |     return PP_ERROR_FAILED; | 
 |   } | 
 |  | 
 |   state_manager_.SetPendingOperation(FileIOStateManager::OPERATION_EXCLUSIVE); | 
 |   return PP_OK_COMPLETIONPENDING; | 
 | } | 
 |  | 
 | int32_t PepperFileIOHost::OnHostMsgFlush( | 
 |     ppapi::host::HostMessageContext* context) { | 
 |   int32_t rv = state_manager_.CheckOperationState( | 
 |       FileIOStateManager::OPERATION_EXCLUSIVE, true); | 
 |   if (rv != PP_OK) | 
 |     return rv; | 
 |  | 
 |   if (!file_.Flush( | 
 |           base::BindOnce(&PepperFileIOHost::ExecutePlatformGeneralCallback, | 
 |                          AsWeakPtr(), context->MakeReplyMessageContext()))) { | 
 |     return PP_ERROR_FAILED; | 
 |   } | 
 |  | 
 |   state_manager_.SetPendingOperation(FileIOStateManager::OPERATION_EXCLUSIVE); | 
 |   return PP_OK_COMPLETIONPENDING; | 
 | } | 
 |  | 
 | int32_t PepperFileIOHost::OnHostMsgClose( | 
 |     ppapi::host::HostMessageContext* context, | 
 |     const ppapi::FileGrowth& file_growth) { | 
 |   if (check_quota_) { | 
 |     file_system_host_->CloseQuotaFile(this, file_growth); | 
 |     check_quota_ = false; | 
 |   } | 
 |  | 
 |   if (file_.IsValid()) { | 
 |     file_.Close(base::BindOnce(&PepperFileIOHost::DidCloseFile, AsWeakPtr())); | 
 |   } | 
 |   return PP_OK; | 
 | } | 
 |  | 
 | void PepperFileIOHost::DidOpenQuotaFile( | 
 |     ppapi::host::ReplyMessageContext reply_context, | 
 |     base::File file, | 
 |     int64_t max_written_offset) { | 
 |   DCHECK(!file_.IsValid()); | 
 |   DCHECK(file.IsValid()); | 
 |   max_written_offset_ = max_written_offset; | 
 |   file_.SetFile(std::move(file)); | 
 |  | 
 |   SendFileOpenReply(reply_context, base::File::FILE_OK); | 
 | } | 
 |  | 
 | void PepperFileIOHost::DidCloseFile(base::File::Error /*error*/) { | 
 |   // Silently ignore if we fail to close the file. | 
 |   if (!on_close_callback_.is_null()) { | 
 |     std::move(on_close_callback_).Run(); | 
 |   } | 
 | } | 
 |  | 
 | int32_t PepperFileIOHost::OnHostMsgRequestOSFileHandle( | 
 |     ppapi::host::HostMessageContext* context) { | 
 |   if (open_flags_ != PP_FILEOPENFLAG_READ && file_system_host_->ChecksQuota()) | 
 |     return PP_ERROR_FAILED; | 
 |  | 
 |   GURL document_url = | 
 |       browser_ppapi_host_->GetDocumentURLForInstance(pp_instance()); | 
 |   BrowserThread::PostTaskAndReplyWithResult( | 
 |       BrowserThread::UI, | 
 |       FROM_HERE, | 
 |       base::Bind(&GetPluginAllowedToCallRequestOSFileHandle, | 
 |                  render_process_id_, | 
 |                  document_url), | 
 |       base::Bind(&PepperFileIOHost::GotPluginAllowedToCallRequestOSFileHandle, | 
 |                  AsWeakPtr(), | 
 |                  context->MakeReplyMessageContext())); | 
 |   return PP_OK_COMPLETIONPENDING; | 
 | } | 
 |  | 
 | void PepperFileIOHost::GotPluginAllowedToCallRequestOSFileHandle( | 
 |     ppapi::host::ReplyMessageContext reply_context, | 
 |     bool plugin_allowed) { | 
 |   DCHECK_CURRENTLY_ON(BrowserThread::IO); | 
 |   if (!browser_ppapi_host_->external_plugin() || | 
 |       host()->permissions().HasPermission(ppapi::PERMISSION_PRIVATE) || | 
 |       plugin_allowed) { | 
 |     if (!AddFileToReplyContext(open_flags_, &reply_context)) | 
 |       reply_context.params.set_result(PP_ERROR_FAILED); | 
 |   } else { | 
 |     reply_context.params.set_result(PP_ERROR_NOACCESS); | 
 |   } | 
 |   host()->SendReply(reply_context, | 
 |                     PpapiPluginMsg_FileIO_RequestOSFileHandleReply()); | 
 | } | 
 |  | 
 | void PepperFileIOHost::ExecutePlatformGeneralCallback( | 
 |     ppapi::host::ReplyMessageContext reply_context, | 
 |     base::File::Error error_code) { | 
 |   reply_context.params.set_result(ppapi::FileErrorToPepperError(error_code)); | 
 |   host()->SendReply(reply_context, PpapiPluginMsg_FileIO_GeneralReply()); | 
 |   state_manager_.SetOperationFinished(); | 
 | } | 
 |  | 
 | void PepperFileIOHost::OnLocalFileOpened( | 
 |     ppapi::host::ReplyMessageContext reply_context, | 
 |     const base::FilePath& path, | 
 |     base::File::Error error_code) { | 
 | #if defined(OS_WIN) || defined(OS_LINUX) | 
 |   // Quarantining a file before its contents are available is only supported on | 
 |   // Windows and Linux. | 
 |   if (!FileOpenForWrite(open_flags_) || error_code != base::File::FILE_OK) { | 
 |     SendFileOpenReply(reply_context, error_code); | 
 |     return; | 
 |   } | 
 |  | 
 |   base::PostTaskAndReplyWithResult( | 
 |       task_runner_.get(), FROM_HERE, | 
 |       base::Bind(&download::QuarantineFile, path, | 
 |                  browser_ppapi_host_->GetDocumentURLForInstance(pp_instance()), | 
 |                  GURL(), std::string()), | 
 |       base::Bind(&PepperFileIOHost::OnLocalFileQuarantined, AsWeakPtr(), | 
 |                  reply_context, path)); | 
 | #else | 
 |   SendFileOpenReply(reply_context, error_code); | 
 | #endif | 
 | } | 
 |  | 
 | #if defined(OS_WIN) || defined(OS_LINUX) | 
 | void PepperFileIOHost::OnLocalFileQuarantined( | 
 |     ppapi::host::ReplyMessageContext reply_context, | 
 |     const base::FilePath& path, | 
 |     download::QuarantineFileResult quarantine_result) { | 
 |   base::File::Error file_error = | 
 |       (quarantine_result == download::QuarantineFileResult::OK | 
 |            ? base::File::FILE_OK | 
 |            : base::File::FILE_ERROR_SECURITY); | 
 |   if (file_error != base::File::FILE_OK && file_.IsValid()) | 
 |     file_.Close(base::FileProxy::StatusCallback()); | 
 |   SendFileOpenReply(reply_context, file_error); | 
 | } | 
 | #endif | 
 |  | 
 | void PepperFileIOHost::SendFileOpenReply( | 
 |     ppapi::host::ReplyMessageContext reply_context, | 
 |     base::File::Error error_code) { | 
 |   int32_t pp_error = ppapi::FileErrorToPepperError(error_code); | 
 |   if (file_.IsValid() && !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(quota_file_system, max_written_offset_)); | 
 |   state_manager_.SetOperationFinished(); | 
 | } | 
 |  | 
 | void PepperFileIOHost::SendOpenErrorReply( | 
 |     ppapi::host::ReplyMessageContext reply_context) { | 
 |   host()->SendReply(reply_context, PpapiPluginMsg_FileIO_OpenReply(0, 0)); | 
 | } | 
 |  | 
 | bool PepperFileIOHost::AddFileToReplyContext( | 
 |     int32_t open_flags, | 
 |     ppapi::host::ReplyMessageContext* reply_context) const { | 
 |   IPC::PlatformFileForTransit transit_file = | 
 |       IPC::GetPlatformFileForTransit(file_.GetPlatformFile(), false); | 
 |   if (transit_file == IPC::InvalidPlatformFileForTransit()) | 
 |     return false; | 
 |  | 
 |   ppapi::proxy::SerializedHandle file_handle; | 
 |   // 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; | 
 | } | 
 |  | 
 | }  // namespace content |