file_io_resource.cc revision f2477e01787aa58f445919b809d89e252beef54f
1726140821f96e3472a8eccef0c67c0b5ad65a1d9Chris Lattner// Copyright (c) 2012 The Chromium Authors. All rights reserved.
2856ba76200ec2302f2fe500bc507f426c7d566c8John Criswell// Use of this source code is governed by a BSD-style license that can be
3856ba76200ec2302f2fe500bc507f426c7d566c8John Criswell// found in the LICENSE file.
4856ba76200ec2302f2fe500bc507f426c7d566c8John Criswell
5856ba76200ec2302f2fe500bc507f426c7d566c8John Criswell#include "ppapi/proxy/file_io_resource.h"
6856ba76200ec2302f2fe500bc507f426c7d566c8John Criswell
7856ba76200ec2302f2fe500bc507f426c7d566c8John Criswell#include "base/bind.h"
8856ba76200ec2302f2fe500bc507f426c7d566c8John Criswell#include "base/task_runner_util.h"
9726140821f96e3472a8eccef0c67c0b5ad65a1d9Chris Lattner#include "ipc/ipc_message.h"
103501feab811c86c9659248a4875fc31a3165f84dChris Lattner#include "ppapi/c/pp_errors.h"
11726140821f96e3472a8eccef0c67c0b5ad65a1d9Chris Lattner#include "ppapi/proxy/ppapi_messages.h"
12726140821f96e3472a8eccef0c67c0b5ad65a1d9Chris Lattner#include "ppapi/shared_impl/array_writer.h"
13726140821f96e3472a8eccef0c67c0b5ad65a1d9Chris Lattner#include "ppapi/shared_impl/file_ref_create_info.h"
14726140821f96e3472a8eccef0c67c0b5ad65a1d9Chris Lattner#include "ppapi/shared_impl/file_system_util.h"
15726140821f96e3472a8eccef0c67c0b5ad65a1d9Chris Lattner#include "ppapi/shared_impl/file_type_conversion.h"
16726140821f96e3472a8eccef0c67c0b5ad65a1d9Chris Lattner#include "ppapi/shared_impl/ppapi_globals.h"
173501feab811c86c9659248a4875fc31a3165f84dChris Lattner#include "ppapi/shared_impl/proxy_lock.h"
18726140821f96e3472a8eccef0c67c0b5ad65a1d9Chris Lattner#include "ppapi/shared_impl/resource_tracker.h"
19726140821f96e3472a8eccef0c67c0b5ad65a1d9Chris Lattner#include "ppapi/thunk/enter.h"
20d0fde30ce850b78371fd1386338350591f9ff494Brian Gaeke#include "ppapi/thunk/ppb_file_ref_api.h"
21d0fde30ce850b78371fd1386338350591f9ff494Brian Gaeke
229d17740295838f94120646ef619b2e187f2d71bdChris Lattnerusing ppapi::thunk::EnterResourceNoLock;
239d17740295838f94120646ef619b2e187f2d71bdChris Lattnerusing ppapi::thunk::PPB_FileIO_API;
249d17740295838f94120646ef619b2e187f2d71bdChris Lattnerusing ppapi::thunk::PPB_FileRef_API;
259d17740295838f94120646ef619b2e187f2d71bdChris Lattner
269d17740295838f94120646ef619b2e187f2d71bdChris Lattnernamespace {
276aab9cf65cd1e96f9d0fa99f8453da454648bba1Chris Lattner
286aab9cf65cd1e96f9d0fa99f8453da454648bba1Chris Lattner// We must allocate a buffer sized according to the request of the plugin. To
296aab9cf65cd1e96f9d0fa99f8453da454648bba1Chris Lattner// reduce the chance of out-of-memory errors, we cap the read size to 32MB.
306aab9cf65cd1e96f9d0fa99f8453da454648bba1Chris Lattner// This is OK since the API specifies that it may perform a partial read.
316aab9cf65cd1e96f9d0fa99f8453da454648bba1Chris Lattnerstatic const int32_t kMaxReadSize = 32 * 1024 * 1024;  // 32MB
324c299f5da1013cd36563a82f188c731b2758074dChris Lattner
334c299f5da1013cd36563a82f188c731b2758074dChris Lattner// An adapter to let Read() share the same implementation with ReadToArray().
344c299f5da1013cd36563a82f188c731b2758074dChris Lattnervoid* DummyGetDataBuffer(void* user_data, uint32_t count, uint32_t size) {
354c299f5da1013cd36563a82f188c731b2758074dChris Lattner  return user_data;
364c299f5da1013cd36563a82f188c731b2758074dChris Lattner}
376aab9cf65cd1e96f9d0fa99f8453da454648bba1Chris Lattner
386aab9cf65cd1e96f9d0fa99f8453da454648bba1Chris Lattner// File thread task to close the file handle.
394c299f5da1013cd36563a82f188c731b2758074dChris Lattnervoid DoClose(base::PlatformFile file) {
406aab9cf65cd1e96f9d0fa99f8453da454648bba1Chris Lattner  base::ClosePlatformFile(file);
416aab9cf65cd1e96f9d0fa99f8453da454648bba1Chris Lattner}
426aab9cf65cd1e96f9d0fa99f8453da454648bba1Chris Lattner
434c299f5da1013cd36563a82f188c731b2758074dChris Lattner}  // namespace
446aab9cf65cd1e96f9d0fa99f8453da454648bba1Chris Lattner
456aab9cf65cd1e96f9d0fa99f8453da454648bba1Chris Lattnernamespace ppapi {
466aab9cf65cd1e96f9d0fa99f8453da454648bba1Chris Lattnernamespace proxy {
476aab9cf65cd1e96f9d0fa99f8453da454648bba1Chris Lattner
484c299f5da1013cd36563a82f188c731b2758074dChris LattnerFileIOResource::QueryOp::QueryOp(scoped_refptr<FileHandleHolder> file_handle)
496aab9cf65cd1e96f9d0fa99f8453da454648bba1Chris Lattner    : file_handle_(file_handle) {
506aab9cf65cd1e96f9d0fa99f8453da454648bba1Chris Lattner  DCHECK(file_handle_);
516aab9cf65cd1e96f9d0fa99f8453da454648bba1Chris Lattner}
526aab9cf65cd1e96f9d0fa99f8453da454648bba1Chris Lattner
534c299f5da1013cd36563a82f188c731b2758074dChris LattnerFileIOResource::QueryOp::~QueryOp() {
546aab9cf65cd1e96f9d0fa99f8453da454648bba1Chris Lattner}
556aab9cf65cd1e96f9d0fa99f8453da454648bba1Chris Lattner
566aab9cf65cd1e96f9d0fa99f8453da454648bba1Chris Lattnerint32_t FileIOResource::QueryOp::DoWork() {
576aab9cf65cd1e96f9d0fa99f8453da454648bba1Chris Lattner  return base::GetPlatformFileInfo(file_handle_->raw_handle(), &file_info_) ?
584c299f5da1013cd36563a82f188c731b2758074dChris Lattner      PP_OK : PP_ERROR_FAILED;
596aab9cf65cd1e96f9d0fa99f8453da454648bba1Chris Lattner}
606aab9cf65cd1e96f9d0fa99f8453da454648bba1Chris Lattner
616aab9cf65cd1e96f9d0fa99f8453da454648bba1Chris LattnerFileIOResource::ReadOp::ReadOp(scoped_refptr<FileHandleHolder> file_handle,
626aab9cf65cd1e96f9d0fa99f8453da454648bba1Chris Lattner                               int64_t offset,
634c299f5da1013cd36563a82f188c731b2758074dChris Lattner                               int32_t bytes_to_read)
646aab9cf65cd1e96f9d0fa99f8453da454648bba1Chris Lattner  : file_handle_(file_handle),
6585b39f229f3146e57d059f1c774400e4bde23987Chris Lattner    offset_(offset),
6685b39f229f3146e57d059f1c774400e4bde23987Chris Lattner    bytes_to_read_(bytes_to_read) {
6785b39f229f3146e57d059f1c774400e4bde23987Chris Lattner  DCHECK(file_handle_);
6885b39f229f3146e57d059f1c774400e4bde23987Chris Lattner}
6985b39f229f3146e57d059f1c774400e4bde23987Chris Lattner
7085b39f229f3146e57d059f1c774400e4bde23987Chris LattnerFileIOResource::ReadOp::~ReadOp() {
7185b39f229f3146e57d059f1c774400e4bde23987Chris Lattner}
7285b39f229f3146e57d059f1c774400e4bde23987Chris Lattner
7385b39f229f3146e57d059f1c774400e4bde23987Chris Lattnerint32_t FileIOResource::ReadOp::DoWork() {
7485b39f229f3146e57d059f1c774400e4bde23987Chris Lattner  DCHECK(!buffer_.get());
7585b39f229f3146e57d059f1c774400e4bde23987Chris Lattner  buffer_.reset(new char[bytes_to_read_]);
7685b39f229f3146e57d059f1c774400e4bde23987Chris Lattner  return base::ReadPlatformFile(
776aab9cf65cd1e96f9d0fa99f8453da454648bba1Chris Lattner      file_handle_->raw_handle(), offset_, buffer_.get(), bytes_to_read_);
7885b39f229f3146e57d059f1c774400e4bde23987Chris Lattner}
796aab9cf65cd1e96f9d0fa99f8453da454648bba1Chris Lattner
806aab9cf65cd1e96f9d0fa99f8453da454648bba1Chris LattnerFileIOResource::FileIOResource(Connection connection, PP_Instance instance)
816aab9cf65cd1e96f9d0fa99f8453da454648bba1Chris Lattner    : PluginResource(connection, instance),
826aab9cf65cd1e96f9d0fa99f8453da454648bba1Chris Lattner      file_system_type_(PP_FILESYSTEMTYPE_INVALID) {
8311e53e3c384e9e25f53a0aec3acf0a725efafeabChris Lattner  SendCreate(BROWSER, PpapiHostMsg_FileIO_Create());
8411e53e3c384e9e25f53a0aec3acf0a725efafeabChris Lattner}
8511e53e3c384e9e25f53a0aec3acf0a725efafeabChris Lattner
862959b6ec49be09096cf0a5e7504d2a1ec15ef2b3Chris LattnerFileIOResource::~FileIOResource() {
874c299f5da1013cd36563a82f188c731b2758074dChris Lattner}
884c299f5da1013cd36563a82f188c731b2758074dChris Lattner
894c299f5da1013cd36563a82f188c731b2758074dChris LattnerPPB_FileIO_API* FileIOResource::AsPPB_FileIO_API() {
904c299f5da1013cd36563a82f188c731b2758074dChris Lattner  return this;
914c299f5da1013cd36563a82f188c731b2758074dChris Lattner}
924c299f5da1013cd36563a82f188c731b2758074dChris Lattner
932959b6ec49be09096cf0a5e7504d2a1ec15ef2b3Chris Lattnerint32_t FileIOResource::Open(PP_Resource file_ref,
942959b6ec49be09096cf0a5e7504d2a1ec15ef2b3Chris Lattner                             int32_t open_flags,
954c299f5da1013cd36563a82f188c731b2758074dChris Lattner                             scoped_refptr<TrackedCallback> callback) {
964c299f5da1013cd36563a82f188c731b2758074dChris Lattner  EnterResourceNoLock<PPB_FileRef_API> enter(file_ref, true);
974c299f5da1013cd36563a82f188c731b2758074dChris Lattner  if (enter.failed())
982959b6ec49be09096cf0a5e7504d2a1ec15ef2b3Chris Lattner    return PP_ERROR_BADRESOURCE;
994c299f5da1013cd36563a82f188c731b2758074dChris Lattner
1004c299f5da1013cd36563a82f188c731b2758074dChris Lattner  PPB_FileRef_API* file_ref_api = enter.object();
1014c299f5da1013cd36563a82f188c731b2758074dChris Lattner  const FileRefCreateInfo& create_info = file_ref_api->GetCreateInfo();
1022959b6ec49be09096cf0a5e7504d2a1ec15ef2b3Chris Lattner  if (!FileSystemTypeIsValid(create_info.file_system_type)) {
1032959b6ec49be09096cf0a5e7504d2a1ec15ef2b3Chris Lattner    NOTREACHED();
1042959b6ec49be09096cf0a5e7504d2a1ec15ef2b3Chris Lattner    return PP_ERROR_FAILED;
1052959b6ec49be09096cf0a5e7504d2a1ec15ef2b3Chris Lattner  }
1064c299f5da1013cd36563a82f188c731b2758074dChris Lattner
1070c514f4e2711ab57bf75f26806f7b8584dfbee6fChris Lattner  int32_t rv = state_manager_.CheckOperationState(
1084c299f5da1013cd36563a82f188c731b2758074dChris Lattner      FileIOStateManager::OPERATION_EXCLUSIVE, false);
1094c299f5da1013cd36563a82f188c731b2758074dChris Lattner  if (rv != PP_OK)
1102959b6ec49be09096cf0a5e7504d2a1ec15ef2b3Chris Lattner    return rv;
1112959b6ec49be09096cf0a5e7504d2a1ec15ef2b3Chris Lattner
1122959b6ec49be09096cf0a5e7504d2a1ec15ef2b3Chris Lattner  file_system_type_ = create_info.file_system_type;
1132959b6ec49be09096cf0a5e7504d2a1ec15ef2b3Chris Lattner  // Keep the FileSystem host alive by taking a reference to its resource. The
1142959b6ec49be09096cf0a5e7504d2a1ec15ef2b3Chris Lattner  // FileIO host uses the FileSystem host for running tasks.
1152959b6ec49be09096cf0a5e7504d2a1ec15ef2b3Chris Lattner  file_system_resource_ = create_info.file_system_plugin_resource;
1162959b6ec49be09096cf0a5e7504d2a1ec15ef2b3Chris Lattner
1172959b6ec49be09096cf0a5e7504d2a1ec15ef2b3Chris Lattner  // Take a reference on the FileRef resource while we're opening the file; we
1182959b6ec49be09096cf0a5e7504d2a1ec15ef2b3Chris Lattner  // don't want the plugin destroying it during the Open operation.
1194c299f5da1013cd36563a82f188c731b2758074dChris Lattner  file_ref_ = enter.resource();
1200c514f4e2711ab57bf75f26806f7b8584dfbee6fChris Lattner
1210c514f4e2711ab57bf75f26806f7b8584dfbee6fChris Lattner  Call<PpapiPluginMsg_FileIO_OpenReply>(BROWSER,
1220c514f4e2711ab57bf75f26806f7b8584dfbee6fChris Lattner      PpapiHostMsg_FileIO_Open(
1232959b6ec49be09096cf0a5e7504d2a1ec15ef2b3Chris Lattner          file_ref,
1242959b6ec49be09096cf0a5e7504d2a1ec15ef2b3Chris Lattner          open_flags),
1252959b6ec49be09096cf0a5e7504d2a1ec15ef2b3Chris Lattner      base::Bind(&FileIOResource::OnPluginMsgOpenFileComplete, this,
1262959b6ec49be09096cf0a5e7504d2a1ec15ef2b3Chris Lattner                 callback));
1270c514f4e2711ab57bf75f26806f7b8584dfbee6fChris Lattner
1282959b6ec49be09096cf0a5e7504d2a1ec15ef2b3Chris Lattner  state_manager_.SetPendingOperation(FileIOStateManager::OPERATION_EXCLUSIVE);
1290c514f4e2711ab57bf75f26806f7b8584dfbee6fChris Lattner  return PP_OK_COMPLETIONPENDING;
1300c514f4e2711ab57bf75f26806f7b8584dfbee6fChris Lattner}
1312959b6ec49be09096cf0a5e7504d2a1ec15ef2b3Chris Lattner
1320c514f4e2711ab57bf75f26806f7b8584dfbee6fChris Lattnerint32_t FileIOResource::Query(PP_FileInfo* info,
1330c514f4e2711ab57bf75f26806f7b8584dfbee6fChris Lattner                              scoped_refptr<TrackedCallback> callback) {
1340c514f4e2711ab57bf75f26806f7b8584dfbee6fChris Lattner  int32_t rv = state_manager_.CheckOperationState(
1350c514f4e2711ab57bf75f26806f7b8584dfbee6fChris Lattner      FileIOStateManager::OPERATION_EXCLUSIVE, true);
1362959b6ec49be09096cf0a5e7504d2a1ec15ef2b3Chris Lattner  if (rv != PP_OK)
1370c514f4e2711ab57bf75f26806f7b8584dfbee6fChris Lattner    return rv;
1380c514f4e2711ab57bf75f26806f7b8584dfbee6fChris Lattner  if (!info)
1390c514f4e2711ab57bf75f26806f7b8584dfbee6fChris Lattner    return PP_ERROR_BADARGUMENT;
1400c514f4e2711ab57bf75f26806f7b8584dfbee6fChris Lattner  if (!FileHandleHolder::IsValid(file_handle_))
1412959b6ec49be09096cf0a5e7504d2a1ec15ef2b3Chris Lattner    return PP_ERROR_FAILED;
1420c514f4e2711ab57bf75f26806f7b8584dfbee6fChris Lattner
1430c514f4e2711ab57bf75f26806f7b8584dfbee6fChris Lattner  state_manager_.SetPendingOperation(FileIOStateManager::OPERATION_EXCLUSIVE);
1442959b6ec49be09096cf0a5e7504d2a1ec15ef2b3Chris Lattner
1450c514f4e2711ab57bf75f26806f7b8584dfbee6fChris Lattner  // If the callback is blocking, perform the task on the calling thread.
146d7908f679eeadc108e09e2aca5faba0b5410ea4aBrian Gaeke  if (callback->is_blocking()) {
1472959b6ec49be09096cf0a5e7504d2a1ec15ef2b3Chris Lattner    int32_t result = PP_ERROR_FAILED;
148d7908f679eeadc108e09e2aca5faba0b5410ea4aBrian Gaeke    base::PlatformFileInfo file_info;
1492959b6ec49be09096cf0a5e7504d2a1ec15ef2b3Chris Lattner    // The plugin could release its reference to this instance when we release
1502959b6ec49be09096cf0a5e7504d2a1ec15ef2b3Chris Lattner    // the proxy lock below.
1512959b6ec49be09096cf0a5e7504d2a1ec15ef2b3Chris Lattner    scoped_refptr<FileIOResource> protect(this);
1529d17740295838f94120646ef619b2e187f2d71bdChris Lattner    {
1539d17740295838f94120646ef619b2e187f2d71bdChris Lattner      // Release the proxy lock while making a potentially slow file call.
1549d17740295838f94120646ef619b2e187f2d71bdChris Lattner      ProxyAutoUnlock unlock;
1553501feab811c86c9659248a4875fc31a3165f84dChris Lattner      if (base::GetPlatformFileInfo(file_handle_->raw_handle(), &file_info))
156726140821f96e3472a8eccef0c67c0b5ad65a1d9Chris Lattner        result = PP_OK;
157726140821f96e3472a8eccef0c67c0b5ad65a1d9Chris Lattner    }
158055c965bff7c8567e7fae90ffe1e10e109856064Chris Lattner    if (result == PP_OK) {
159726140821f96e3472a8eccef0c67c0b5ad65a1d9Chris Lattner      // This writes the file info into the plugin's PP_FileInfo struct.
1603501feab811c86c9659248a4875fc31a3165f84dChris Lattner      ppapi::PlatformFileInfoToPepperFileInfo(file_info,
161726140821f96e3472a8eccef0c67c0b5ad65a1d9Chris Lattner                                              file_system_type_,
162726140821f96e3472a8eccef0c67c0b5ad65a1d9Chris Lattner                                              info);
163726140821f96e3472a8eccef0c67c0b5ad65a1d9Chris Lattner    }
164726140821f96e3472a8eccef0c67c0b5ad65a1d9Chris Lattner    state_manager_.SetOperationFinished();
165726140821f96e3472a8eccef0c67c0b5ad65a1d9Chris Lattner    return result;
166e9d883828ad92f3a1d06e3c9e98c4e3df937197dMisha Brukman  }
167e9d883828ad92f3a1d06e3c9e98c4e3df937197dMisha Brukman
168e9d883828ad92f3a1d06e3c9e98c4e3df937197dMisha Brukman  // For the non-blocking case, post a task to the file thread and add a
169e9d883828ad92f3a1d06e3c9e98c4e3df937197dMisha Brukman  // completion task to write the result.
170e9d883828ad92f3a1d06e3c9e98c4e3df937197dMisha Brukman  scoped_refptr<QueryOp> query_op(new QueryOp(file_handle_));
171e9d883828ad92f3a1d06e3c9e98c4e3df937197dMisha Brukman  base::PostTaskAndReplyWithResult(
17212745c55e1d5a6e76d41684f1b507ea7c6b888acMisha Brukman      PpapiGlobals::Get()->GetFileTaskRunner(),
17312745c55e1d5a6e76d41684f1b507ea7c6b888acMisha Brukman      FROM_HERE,
17412745c55e1d5a6e76d41684f1b507ea7c6b888acMisha Brukman      Bind(&FileIOResource::QueryOp::DoWork, query_op),
175e9d883828ad92f3a1d06e3c9e98c4e3df937197dMisha Brukman      RunWhileLocked(Bind(&TrackedCallback::Run, callback)));
176e9d883828ad92f3a1d06e3c9e98c4e3df937197dMisha Brukman  callback->set_completion_task(
177e9d883828ad92f3a1d06e3c9e98c4e3df937197dMisha Brukman      Bind(&FileIOResource::OnQueryComplete, this, query_op, info));
178f21dfcddcf199444440004bfa74bb222e2d3ce9eChris Lattner
179f21dfcddcf199444440004bfa74bb222e2d3ce9eChris Lattner  return PP_OK_COMPLETIONPENDING;
180f21dfcddcf199444440004bfa74bb222e2d3ce9eChris Lattner}
1814d18d5ce1e62779e7736ca0811e2e1cb06e4ea36Chris Lattner
1824d18d5ce1e62779e7736ca0811e2e1cb06e4ea36Chris Lattnerint32_t FileIOResource::Touch(PP_Time last_access_time,
1834d18d5ce1e62779e7736ca0811e2e1cb06e4ea36Chris Lattner                              PP_Time last_modified_time,
184726140821f96e3472a8eccef0c67c0b5ad65a1d9Chris Lattner                              scoped_refptr<TrackedCallback> callback) {
185726140821f96e3472a8eccef0c67c0b5ad65a1d9Chris Lattner  int32_t rv = state_manager_.CheckOperationState(
186d0fde30ce850b78371fd1386338350591f9ff494Brian Gaeke      FileIOStateManager::OPERATION_EXCLUSIVE, true);
187d0fde30ce850b78371fd1386338350591f9ff494Brian Gaeke  if (rv != PP_OK)
188726140821f96e3472a8eccef0c67c0b5ad65a1d9Chris Lattner    return rv;
189
190  Call<PpapiPluginMsg_FileIO_GeneralReply>(BROWSER,
191      PpapiHostMsg_FileIO_Touch(last_access_time, last_modified_time),
192      base::Bind(&FileIOResource::OnPluginMsgGeneralComplete, this,
193                 callback));
194
195  state_manager_.SetPendingOperation(FileIOStateManager::OPERATION_EXCLUSIVE);
196  return PP_OK_COMPLETIONPENDING;
197}
198
199int32_t FileIOResource::Read(int64_t offset,
200                             char* buffer,
201                             int32_t bytes_to_read,
202                             scoped_refptr<TrackedCallback> callback) {
203  int32_t rv = state_manager_.CheckOperationState(
204      FileIOStateManager::OPERATION_READ, true);
205  if (rv != PP_OK)
206    return rv;
207
208  PP_ArrayOutput output_adapter;
209  output_adapter.GetDataBuffer = &DummyGetDataBuffer;
210  output_adapter.user_data = buffer;
211  return ReadValidated(offset, bytes_to_read, output_adapter, callback);
212}
213
214int32_t FileIOResource::ReadToArray(int64_t offset,
215                                    int32_t max_read_length,
216                                    PP_ArrayOutput* array_output,
217                                    scoped_refptr<TrackedCallback> callback) {
218  DCHECK(array_output);
219  int32_t rv = state_manager_.CheckOperationState(
220      FileIOStateManager::OPERATION_READ, true);
221  if (rv != PP_OK)
222    return rv;
223
224  return ReadValidated(offset, max_read_length, *array_output, callback);
225}
226
227int32_t FileIOResource::Write(int64_t offset,
228                              const char* buffer,
229                              int32_t bytes_to_write,
230                              scoped_refptr<TrackedCallback> callback) {
231  int32_t rv = state_manager_.CheckOperationState(
232      FileIOStateManager::OPERATION_WRITE, true);
233  if (rv != PP_OK)
234    return rv;
235
236  // TODO(brettw) it would be nice to use a shared memory buffer for large
237  // writes rather than having to copy to a string (which will involve a number
238  // of extra copies to serialize over IPC).
239  Call<PpapiPluginMsg_FileIO_GeneralReply>(BROWSER,
240      PpapiHostMsg_FileIO_Write(offset, std::string(buffer, bytes_to_write)),
241      base::Bind(&FileIOResource::OnPluginMsgGeneralComplete, this,
242                 callback));
243
244  state_manager_.SetPendingOperation(FileIOStateManager::OPERATION_WRITE);
245  return PP_OK_COMPLETIONPENDING;
246}
247
248int32_t FileIOResource::SetLength(int64_t length,
249                                  scoped_refptr<TrackedCallback> callback) {
250  int32_t rv = state_manager_.CheckOperationState(
251      FileIOStateManager::OPERATION_EXCLUSIVE, true);
252  if (rv != PP_OK)
253    return rv;
254
255  Call<PpapiPluginMsg_FileIO_GeneralReply>(BROWSER,
256      PpapiHostMsg_FileIO_SetLength(length),
257      base::Bind(&FileIOResource::OnPluginMsgGeneralComplete, this,
258                 callback));
259
260  state_manager_.SetPendingOperation(FileIOStateManager::OPERATION_EXCLUSIVE);
261  return PP_OK_COMPLETIONPENDING;
262}
263
264int32_t FileIOResource::Flush(scoped_refptr<TrackedCallback> callback) {
265  int32_t rv = state_manager_.CheckOperationState(
266      FileIOStateManager::OPERATION_EXCLUSIVE, true);
267  if (rv != PP_OK)
268    return rv;
269
270  Call<PpapiPluginMsg_FileIO_GeneralReply>(BROWSER,
271      PpapiHostMsg_FileIO_Flush(),
272      base::Bind(&FileIOResource::OnPluginMsgGeneralComplete, this,
273                 callback));
274
275  state_manager_.SetPendingOperation(FileIOStateManager::OPERATION_EXCLUSIVE);
276  return PP_OK_COMPLETIONPENDING;
277}
278
279void FileIOResource::Close() {
280  if (file_handle_) {
281    file_handle_ = NULL;
282  }
283  Post(BROWSER, PpapiHostMsg_FileIO_Close());
284}
285
286int32_t FileIOResource::RequestOSFileHandle(
287    PP_FileHandle* handle,
288    scoped_refptr<TrackedCallback> callback) {
289  int32_t rv = state_manager_.CheckOperationState(
290      FileIOStateManager::OPERATION_EXCLUSIVE, true);
291  if (rv != PP_OK)
292    return rv;
293
294  Call<PpapiPluginMsg_FileIO_RequestOSFileHandleReply>(BROWSER,
295      PpapiHostMsg_FileIO_RequestOSFileHandle(),
296      base::Bind(&FileIOResource::OnPluginMsgRequestOSFileHandleComplete, this,
297                 callback, handle));
298
299  state_manager_.SetPendingOperation(FileIOStateManager::OPERATION_EXCLUSIVE);
300  return PP_OK_COMPLETIONPENDING;
301}
302
303FileIOResource::FileHandleHolder::FileHandleHolder(PP_FileHandle file_handle)
304    : raw_handle_(file_handle) {
305}
306
307// static
308bool FileIOResource::FileHandleHolder::IsValid(
309    const scoped_refptr<FileIOResource::FileHandleHolder>& handle) {
310  return handle && (handle->raw_handle() != base::kInvalidPlatformFileValue);
311}
312
313FileIOResource::FileHandleHolder::~FileHandleHolder() {
314  if (raw_handle_ != base::kInvalidPlatformFileValue) {
315    base::TaskRunner* file_task_runner =
316        PpapiGlobals::Get()->GetFileTaskRunner();
317    file_task_runner->PostTask(FROM_HERE,
318                               base::Bind(&DoClose, raw_handle_));
319  }
320}
321
322int32_t FileIOResource::ReadValidated(int64_t offset,
323                                      int32_t bytes_to_read,
324                                      const PP_ArrayOutput& array_output,
325                                      scoped_refptr<TrackedCallback> callback) {
326  if (bytes_to_read < 0)
327    return PP_ERROR_FAILED;
328  if (!FileHandleHolder::IsValid(file_handle_))
329    return PP_ERROR_FAILED;
330
331  state_manager_.SetPendingOperation(FileIOStateManager::OPERATION_READ);
332
333  bytes_to_read = std::min(bytes_to_read, kMaxReadSize);
334  if (callback->is_blocking()) {
335    char* buffer = static_cast<char*>(
336        array_output.GetDataBuffer(array_output.user_data, bytes_to_read, 1));
337    int32_t result = PP_ERROR_FAILED;
338    // The plugin could release its reference to this instance when we release
339    // the proxy lock below.
340    scoped_refptr<FileIOResource> protect(this);
341    if (buffer) {
342      // Release the proxy lock while making a potentially slow file call.
343      ProxyAutoUnlock unlock;
344      result = base::ReadPlatformFile(
345          file_handle_->raw_handle(), offset, buffer, bytes_to_read);
346      if (result < 0)
347        result = PP_ERROR_FAILED;
348    }
349    state_manager_.SetOperationFinished();
350    return result;
351  }
352
353  // For the non-blocking case, post a task to the file thread.
354  scoped_refptr<ReadOp> read_op(
355      new ReadOp(file_handle_, offset, bytes_to_read));
356  base::PostTaskAndReplyWithResult(
357      PpapiGlobals::Get()->GetFileTaskRunner(),
358      FROM_HERE,
359      Bind(&FileIOResource::ReadOp::DoWork, read_op),
360      RunWhileLocked(Bind(&TrackedCallback::Run, callback)));
361  callback->set_completion_task(
362      Bind(&FileIOResource::OnReadComplete, this, read_op, array_output));
363
364  return PP_OK_COMPLETIONPENDING;
365}
366
367int32_t FileIOResource::OnQueryComplete(scoped_refptr<QueryOp> query_op,
368                                        PP_FileInfo* info,
369                                        int32_t result) {
370  DCHECK(state_manager_.get_pending_operation() ==
371         FileIOStateManager::OPERATION_EXCLUSIVE);
372
373  if (result == PP_OK) {
374    // This writes the file info into the plugin's PP_FileInfo struct.
375    ppapi::PlatformFileInfoToPepperFileInfo(query_op->file_info(),
376                                            file_system_type_,
377                                            info);
378  }
379  state_manager_.SetOperationFinished();
380  return result;
381}
382
383int32_t FileIOResource::OnReadComplete(scoped_refptr<ReadOp> read_op,
384                                       PP_ArrayOutput array_output,
385                                       int32_t result) {
386  DCHECK(state_manager_.get_pending_operation() ==
387         FileIOStateManager::OPERATION_READ);
388  if (result >= 0) {
389    ArrayWriter output;
390    output.set_pp_array_output(array_output);
391    if (output.is_valid())
392      output.StoreArray(read_op->buffer(), result);
393    else
394      result = PP_ERROR_FAILED;
395  } else {
396    // The read operation failed.
397    result = PP_ERROR_FAILED;
398  }
399  state_manager_.SetOperationFinished();
400  return result;
401}
402
403void FileIOResource::OnPluginMsgGeneralComplete(
404    scoped_refptr<TrackedCallback> callback,
405    const ResourceMessageReplyParams& params) {
406  DCHECK(state_manager_.get_pending_operation() ==
407         FileIOStateManager::OPERATION_EXCLUSIVE ||
408         state_manager_.get_pending_operation() ==
409         FileIOStateManager::OPERATION_WRITE);
410  // End this operation now, so the user's callback can execute another FileIO
411  // operation, assuming there are no other pending operations.
412  state_manager_.SetOperationFinished();
413  callback->Run(params.result());
414}
415
416void FileIOResource::OnPluginMsgOpenFileComplete(
417    scoped_refptr<TrackedCallback> callback,
418    const ResourceMessageReplyParams& params) {
419  DCHECK(state_manager_.get_pending_operation() ==
420         FileIOStateManager::OPERATION_EXCLUSIVE);
421
422  // Release the FileRef resource.
423  file_ref_ = NULL;
424  if (params.result() == PP_OK)
425    state_manager_.SetOpenSucceed();
426
427  int32_t result = params.result();
428  IPC::PlatformFileForTransit transit_file;
429  if ((result == PP_OK) && params.TakeFileHandleAtIndex(0, &transit_file)) {
430    file_handle_ = new FileHandleHolder(
431        IPC::PlatformFileForTransitToPlatformFile(transit_file));
432  }
433  // End this operation now, so the user's callback can execute another FileIO
434  // operation, assuming there are no other pending operations.
435  state_manager_.SetOperationFinished();
436  callback->Run(result);
437}
438
439void FileIOResource::OnPluginMsgRequestOSFileHandleComplete(
440    scoped_refptr<TrackedCallback> callback,
441    PP_FileHandle* output_handle,
442    const ResourceMessageReplyParams& params) {
443  DCHECK(state_manager_.get_pending_operation() ==
444         FileIOStateManager::OPERATION_EXCLUSIVE);
445
446  if (!TrackedCallback::IsPending(callback)) {
447    state_manager_.SetOperationFinished();
448    return;
449  }
450
451  int32_t result = params.result();
452  IPC::PlatformFileForTransit transit_file;
453  if (!params.TakeFileHandleAtIndex(0, &transit_file))
454    result = PP_ERROR_FAILED;
455  *output_handle = IPC::PlatformFileForTransitToPlatformFile(transit_file);
456
457  // End this operation now, so the user's callback can execute another FileIO
458  // operation, assuming there are no other pending operations.
459  state_manager_.SetOperationFinished();
460  callback->Run(result);
461}
462
463}  // namespace proxy
464}  // namespace ppapi
465