Home | History | Annotate | Download | only in proxy
      1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 #include "ppapi/proxy/file_io_resource.h"
      6 
      7 #include "base/bind.h"
      8 #include "base/task_runner_util.h"
      9 #include "ipc/ipc_message.h"
     10 #include "ppapi/c/pp_errors.h"
     11 #include "ppapi/proxy/ppapi_messages.h"
     12 #include "ppapi/shared_impl/array_writer.h"
     13 #include "ppapi/shared_impl/file_type_conversion.h"
     14 #include "ppapi/shared_impl/ppapi_globals.h"
     15 #include "ppapi/shared_impl/proxy_lock.h"
     16 #include "ppapi/shared_impl/resource_tracker.h"
     17 #include "ppapi/thunk/enter.h"
     18 #include "ppapi/thunk/ppb_file_ref_api.h"
     19 
     20 using ppapi::thunk::EnterResourceNoLock;
     21 using ppapi::thunk::PPB_FileIO_API;
     22 using ppapi::thunk::PPB_FileRef_API;
     23 
     24 namespace {
     25 
     26 // We must allocate a buffer sized according to the request of the plugin. To
     27 // reduce the chance of out-of-memory errors, we cap the read size to 32MB.
     28 // This is OK since the API specifies that it may perform a partial read.
     29 static const int32_t kMaxReadSize = 32 * 1024 * 1024;  // 32MB
     30 
     31 // An adapter to let Read() share the same implementation with ReadToArray().
     32 void* DummyGetDataBuffer(void* user_data, uint32_t count, uint32_t size) {
     33   return user_data;
     34 }
     35 
     36 // File thread task to close the file handle.
     37 void DoClose(base::PlatformFile file) {
     38   base::ClosePlatformFile(file);
     39 }
     40 
     41 }  // namespace
     42 
     43 namespace ppapi {
     44 namespace proxy {
     45 
     46 FileIOResource::QueryOp::QueryOp(PP_FileHandle file_handle)
     47     : file_handle_(file_handle) {
     48 }
     49 
     50 FileIOResource::QueryOp::~QueryOp() {
     51 }
     52 
     53 int32_t FileIOResource::QueryOp::DoWork() {
     54   return base::GetPlatformFileInfo(file_handle_, &file_info_) ?
     55       PP_OK : PP_ERROR_FAILED;
     56 }
     57 
     58 FileIOResource::ReadOp::ReadOp(PP_FileHandle file_handle,
     59                                int64_t offset,
     60                                int32_t bytes_to_read)
     61   : file_handle_(file_handle),
     62     offset_(offset),
     63     bytes_to_read_(bytes_to_read) {
     64 }
     65 
     66 FileIOResource::ReadOp::~ReadOp() {
     67 }
     68 
     69 int32_t FileIOResource::ReadOp::DoWork() {
     70   DCHECK(!buffer_.get());
     71   buffer_.reset(new char[bytes_to_read_]);
     72   return base::ReadPlatformFile(
     73       file_handle_, offset_, buffer_.get(), bytes_to_read_);
     74 }
     75 
     76 FileIOResource::FileIOResource(Connection connection, PP_Instance instance)
     77     : PluginResource(connection, instance),
     78       file_handle_(base::kInvalidPlatformFileValue),
     79       file_system_type_(PP_FILESYSTEMTYPE_INVALID) {
     80   SendCreate(RENDERER, PpapiHostMsg_FileIO_Create());
     81 }
     82 
     83 FileIOResource::~FileIOResource() {
     84   CloseFileHandle();
     85 }
     86 
     87 PPB_FileIO_API* FileIOResource::AsPPB_FileIO_API() {
     88   return this;
     89 }
     90 
     91 int32_t FileIOResource::Open(PP_Resource file_ref,
     92                              int32_t open_flags,
     93                              scoped_refptr<TrackedCallback> callback) {
     94   EnterResourceNoLock<PPB_FileRef_API> enter(file_ref, true);
     95   if (enter.failed())
     96     return PP_ERROR_BADRESOURCE;
     97 
     98   PPB_FileRef_API* file_ref_api = enter.object();
     99   PP_FileSystemType type = file_ref_api->GetFileSystemType();
    100   if (type != PP_FILESYSTEMTYPE_LOCALPERSISTENT &&
    101       type != PP_FILESYSTEMTYPE_LOCALTEMPORARY &&
    102       type != PP_FILESYSTEMTYPE_EXTERNAL &&
    103       type != PP_FILESYSTEMTYPE_ISOLATED) {
    104     NOTREACHED();
    105     return PP_ERROR_FAILED;
    106   }
    107   file_system_type_ = type;
    108 
    109   int32_t rv = state_manager_.CheckOperationState(
    110       FileIOStateManager::OPERATION_EXCLUSIVE, false);
    111   if (rv != PP_OK)
    112     return rv;
    113 
    114   Call<PpapiPluginMsg_FileIO_OpenReply>(RENDERER,
    115       PpapiHostMsg_FileIO_Open(
    116           enter.resource()->host_resource().host_resource(),
    117           open_flags),
    118       base::Bind(&FileIOResource::OnPluginMsgOpenFileComplete, this,
    119                  callback));
    120 
    121   state_manager_.SetPendingOperation(FileIOStateManager::OPERATION_EXCLUSIVE);
    122   return PP_OK_COMPLETIONPENDING;
    123 }
    124 
    125 int32_t FileIOResource::Query(PP_FileInfo* info,
    126                               scoped_refptr<TrackedCallback> callback) {
    127   int32_t rv = state_manager_.CheckOperationState(
    128       FileIOStateManager::OPERATION_EXCLUSIVE, true);
    129   if (rv != PP_OK)
    130     return rv;
    131   if (!info)
    132     return PP_ERROR_BADARGUMENT;
    133   if (file_handle_ == base::kInvalidPlatformFileValue)
    134     return PP_ERROR_FAILED;
    135 
    136   state_manager_.SetPendingOperation(FileIOStateManager::OPERATION_EXCLUSIVE);
    137   scoped_refptr<QueryOp> query_op(new QueryOp(file_handle_));
    138 
    139   // If the callback is blocking, perform the task on the calling thread.
    140   if (callback->is_blocking()) {
    141     int32_t result;
    142     {
    143       // Release the proxy lock while making a potentially slow file call.
    144       ProxyAutoUnlock unlock;
    145       result = query_op->DoWork();
    146     }
    147     return OnQueryComplete(query_op, info, result);
    148   }
    149 
    150   // For the non-blocking case, post a task to the file thread and add a
    151   // completion task to write the result.
    152   base::PostTaskAndReplyWithResult(
    153       PpapiGlobals::Get()->GetFileTaskRunner(pp_instance()),
    154       FROM_HERE,
    155       Bind(&FileIOResource::QueryOp::DoWork, query_op),
    156       RunWhileLocked(Bind(&TrackedCallback::Run, callback)));
    157   callback->set_completion_task(
    158       Bind(&FileIOResource::OnQueryComplete, this, query_op, info));
    159 
    160   return PP_OK_COMPLETIONPENDING;
    161 }
    162 
    163 int32_t FileIOResource::Touch(PP_Time last_access_time,
    164                               PP_Time last_modified_time,
    165                               scoped_refptr<TrackedCallback> callback) {
    166   int32_t rv = state_manager_.CheckOperationState(
    167       FileIOStateManager::OPERATION_EXCLUSIVE, true);
    168   if (rv != PP_OK)
    169     return rv;
    170 
    171   Call<PpapiPluginMsg_FileIO_GeneralReply>(RENDERER,
    172       PpapiHostMsg_FileIO_Touch(last_access_time, last_modified_time),
    173       base::Bind(&FileIOResource::OnPluginMsgGeneralComplete, this,
    174                  callback));
    175 
    176   state_manager_.SetPendingOperation(FileIOStateManager::OPERATION_EXCLUSIVE);
    177   return PP_OK_COMPLETIONPENDING;
    178 }
    179 
    180 int32_t FileIOResource::Read(int64_t offset,
    181                              char* buffer,
    182                              int32_t bytes_to_read,
    183                              scoped_refptr<TrackedCallback> callback) {
    184   int32_t rv = state_manager_.CheckOperationState(
    185       FileIOStateManager::OPERATION_READ, true);
    186   if (rv != PP_OK)
    187     return rv;
    188 
    189   PP_ArrayOutput output_adapter;
    190   output_adapter.GetDataBuffer = &DummyGetDataBuffer;
    191   output_adapter.user_data = buffer;
    192   return ReadValidated(offset, bytes_to_read, output_adapter, callback);
    193 }
    194 
    195 int32_t FileIOResource::ReadToArray(int64_t offset,
    196                                     int32_t max_read_length,
    197                                     PP_ArrayOutput* array_output,
    198                                     scoped_refptr<TrackedCallback> callback) {
    199   DCHECK(array_output);
    200   int32_t rv = state_manager_.CheckOperationState(
    201       FileIOStateManager::OPERATION_READ, true);
    202   if (rv != PP_OK)
    203     return rv;
    204 
    205   return ReadValidated(offset, max_read_length, *array_output, callback);
    206 }
    207 
    208 int32_t FileIOResource::Write(int64_t offset,
    209                               const char* buffer,
    210                               int32_t bytes_to_write,
    211                               scoped_refptr<TrackedCallback> callback) {
    212   int32_t rv = state_manager_.CheckOperationState(
    213       FileIOStateManager::OPERATION_WRITE, true);
    214   if (rv != PP_OK)
    215     return rv;
    216 
    217   // TODO(brettw) it would be nice to use a shared memory buffer for large
    218   // writes rather than having to copy to a string (which will involve a number
    219   // of extra copies to serialize over IPC).
    220   Call<PpapiPluginMsg_FileIO_GeneralReply>(RENDERER,
    221       PpapiHostMsg_FileIO_Write(offset, std::string(buffer, bytes_to_write)),
    222       base::Bind(&FileIOResource::OnPluginMsgGeneralComplete, this,
    223                  callback));
    224 
    225   state_manager_.SetPendingOperation(FileIOStateManager::OPERATION_WRITE);
    226   return PP_OK_COMPLETIONPENDING;
    227 }
    228 
    229 int32_t FileIOResource::SetLength(int64_t length,
    230                                   scoped_refptr<TrackedCallback> callback) {
    231   int32_t rv = state_manager_.CheckOperationState(
    232       FileIOStateManager::OPERATION_EXCLUSIVE, true);
    233   if (rv != PP_OK)
    234     return rv;
    235 
    236   Call<PpapiPluginMsg_FileIO_GeneralReply>(RENDERER,
    237       PpapiHostMsg_FileIO_SetLength(length),
    238       base::Bind(&FileIOResource::OnPluginMsgGeneralComplete, this,
    239                  callback));
    240 
    241   state_manager_.SetPendingOperation(FileIOStateManager::OPERATION_EXCLUSIVE);
    242   return PP_OK_COMPLETIONPENDING;
    243 }
    244 
    245 int32_t FileIOResource::Flush(scoped_refptr<TrackedCallback> callback) {
    246   int32_t rv = state_manager_.CheckOperationState(
    247       FileIOStateManager::OPERATION_EXCLUSIVE, true);
    248   if (rv != PP_OK)
    249     return rv;
    250 
    251   Call<PpapiPluginMsg_FileIO_GeneralReply>(RENDERER,
    252       PpapiHostMsg_FileIO_Flush(),
    253       base::Bind(&FileIOResource::OnPluginMsgGeneralComplete, this,
    254                  callback));
    255 
    256   state_manager_.SetPendingOperation(FileIOStateManager::OPERATION_EXCLUSIVE);
    257   return PP_OK_COMPLETIONPENDING;
    258 }
    259 
    260 void FileIOResource::Close() {
    261   CloseFileHandle();
    262   Post(RENDERER, PpapiHostMsg_FileIO_Close());
    263 }
    264 
    265 int32_t FileIOResource::GetOSFileDescriptor() {
    266   int32_t file_descriptor;
    267   // Only available when running in process.
    268   SyncCall<PpapiPluginMsg_FileIO_GetOSFileDescriptorReply>(
    269       RENDERER, PpapiHostMsg_FileIO_GetOSFileDescriptor(), &file_descriptor);
    270   return file_descriptor;
    271 }
    272 
    273 int32_t FileIOResource::RequestOSFileHandle(
    274     PP_FileHandle* handle,
    275     scoped_refptr<TrackedCallback> callback) {
    276   int32_t rv = state_manager_.CheckOperationState(
    277       FileIOStateManager::OPERATION_EXCLUSIVE, true);
    278   if (rv != PP_OK)
    279     return rv;
    280 
    281   Call<PpapiPluginMsg_FileIO_RequestOSFileHandleReply>(RENDERER,
    282       PpapiHostMsg_FileIO_RequestOSFileHandle(),
    283       base::Bind(&FileIOResource::OnPluginMsgRequestOSFileHandleComplete, this,
    284                  callback, handle));
    285 
    286   state_manager_.SetPendingOperation(FileIOStateManager::OPERATION_EXCLUSIVE);
    287   return PP_OK_COMPLETIONPENDING;
    288 }
    289 
    290 int32_t FileIOResource::WillWrite(int64_t offset,
    291                                   int32_t bytes_to_write,
    292                                   scoped_refptr<TrackedCallback> callback) {
    293   Call<PpapiPluginMsg_FileIO_GeneralReply>(RENDERER,
    294       PpapiHostMsg_FileIO_WillWrite(offset, bytes_to_write),
    295       base::Bind(&FileIOResource::OnPluginMsgGeneralComplete, this, callback));
    296 
    297   state_manager_.SetPendingOperation(FileIOStateManager::OPERATION_EXCLUSIVE);
    298   return PP_OK_COMPLETIONPENDING;
    299 }
    300 
    301 int32_t FileIOResource::WillSetLength(int64_t length,
    302                                       scoped_refptr<TrackedCallback> callback) {
    303   Call<PpapiPluginMsg_FileIO_GeneralReply>(RENDERER,
    304       PpapiHostMsg_FileIO_WillSetLength(length),
    305       base::Bind(&FileIOResource::OnPluginMsgGeneralComplete, this, callback));
    306 
    307   state_manager_.SetPendingOperation(FileIOStateManager::OPERATION_EXCLUSIVE);
    308   return PP_OK_COMPLETIONPENDING;
    309 }
    310 
    311 int32_t FileIOResource::ReadValidated(int64_t offset,
    312                                       int32_t bytes_to_read,
    313                                       const PP_ArrayOutput& array_output,
    314                                       scoped_refptr<TrackedCallback> callback) {
    315   if (bytes_to_read < 0)
    316     return PP_ERROR_FAILED;
    317   if (file_handle_ == base::kInvalidPlatformFileValue)
    318     return PP_ERROR_FAILED;
    319 
    320   state_manager_.SetPendingOperation(FileIOStateManager::OPERATION_READ);
    321 
    322   bytes_to_read = std::min(bytes_to_read, kMaxReadSize);
    323   scoped_refptr<ReadOp> read_op(
    324       new ReadOp(file_handle_, offset, bytes_to_read));
    325   if (callback->is_blocking()) {
    326     int32_t result;
    327     {
    328       // Release the proxy lock while making a potentially slow file call.
    329       ProxyAutoUnlock unlock;
    330       result = read_op->DoWork();
    331     }
    332     return OnReadComplete(read_op, array_output, result);
    333   }
    334 
    335   // For the non-blocking case, post a task to the file thread.
    336   base::PostTaskAndReplyWithResult(
    337       PpapiGlobals::Get()->GetFileTaskRunner(pp_instance()),
    338       FROM_HERE,
    339       Bind(&FileIOResource::ReadOp::DoWork, read_op),
    340       RunWhileLocked(Bind(&TrackedCallback::Run, callback)));
    341   callback->set_completion_task(
    342       Bind(&FileIOResource::OnReadComplete, this, read_op, array_output));
    343 
    344   return PP_OK_COMPLETIONPENDING;
    345 }
    346 
    347 void FileIOResource::CloseFileHandle() {
    348   if (file_handle_ != base::kInvalidPlatformFileValue) {
    349     // Close our local fd on the file thread.
    350     base::TaskRunner* file_task_runner =
    351         PpapiGlobals::Get()->GetFileTaskRunner(pp_instance());
    352     file_task_runner->PostTask(FROM_HERE,
    353                                base::Bind(&DoClose, file_handle_));
    354 
    355     file_handle_ = base::kInvalidPlatformFileValue;
    356   }
    357 }
    358 
    359 int32_t FileIOResource::OnQueryComplete(scoped_refptr<QueryOp> query_op,
    360                                         PP_FileInfo* info,
    361                                         int32_t result) {
    362   DCHECK(state_manager_.get_pending_operation() ==
    363          FileIOStateManager::OPERATION_EXCLUSIVE);
    364 
    365   if (result == PP_OK) {
    366     // This writes the file info into the plugin's PP_FileInfo struct.
    367     ppapi::PlatformFileInfoToPepperFileInfo(query_op->file_info(),
    368                                             file_system_type_,
    369                                             info);
    370   }
    371   state_manager_.SetOperationFinished();
    372   return result;
    373 }
    374 
    375 int32_t FileIOResource::OnReadComplete(scoped_refptr<ReadOp> read_op,
    376                                        PP_ArrayOutput array_output,
    377                                        int32_t result) {
    378   DCHECK(state_manager_.get_pending_operation() ==
    379          FileIOStateManager::OPERATION_READ);
    380   if (result >= 0) {
    381     ArrayWriter output;
    382     output.set_pp_array_output(array_output);
    383     if (output.is_valid())
    384       output.StoreArray(read_op->buffer(), result);
    385     else
    386       result = PP_ERROR_FAILED;
    387   } else {
    388     // The read operation failed.
    389     result = PP_ERROR_FAILED;
    390   }
    391   state_manager_.SetOperationFinished();
    392   return result;
    393 }
    394 
    395 void FileIOResource::OnPluginMsgGeneralComplete(
    396     scoped_refptr<TrackedCallback> callback,
    397     const ResourceMessageReplyParams& params) {
    398   DCHECK(state_manager_.get_pending_operation() ==
    399          FileIOStateManager::OPERATION_EXCLUSIVE ||
    400          state_manager_.get_pending_operation() ==
    401          FileIOStateManager::OPERATION_WRITE);
    402   // End this operation now, so the user's callback can execute another FileIO
    403   // operation, assuming there are no other pending operations.
    404   state_manager_.SetOperationFinished();
    405   callback->Run(params.result());
    406 }
    407 
    408 void FileIOResource::OnPluginMsgOpenFileComplete(
    409     scoped_refptr<TrackedCallback> callback,
    410     const ResourceMessageReplyParams& params) {
    411   DCHECK(state_manager_.get_pending_operation() ==
    412          FileIOStateManager::OPERATION_EXCLUSIVE);
    413   if (params.result() == PP_OK)
    414     state_manager_.SetOpenSucceed();
    415 
    416   int32_t result = params.result();
    417   IPC::PlatformFileForTransit transit_file;
    418   if ((result == PP_OK) && params.TakeFileHandleAtIndex(0, &transit_file))
    419     file_handle_ = IPC::PlatformFileForTransitToPlatformFile(transit_file);
    420   // End this operation now, so the user's callback can execute another FileIO
    421   // operation, assuming there are no other pending operations.
    422   state_manager_.SetOperationFinished();
    423   callback->Run(result);
    424 }
    425 
    426 void FileIOResource::OnPluginMsgRequestOSFileHandleComplete(
    427     scoped_refptr<TrackedCallback> callback,
    428     PP_FileHandle* output_handle,
    429     const ResourceMessageReplyParams& params) {
    430   DCHECK(state_manager_.get_pending_operation() ==
    431          FileIOStateManager::OPERATION_EXCLUSIVE);
    432 
    433   if (!TrackedCallback::IsPending(callback)) {
    434     state_manager_.SetOperationFinished();
    435     return;
    436   }
    437 
    438   int32_t result = params.result();
    439   IPC::PlatformFileForTransit transit_file;
    440   if (!params.TakeFileHandleAtIndex(0, &transit_file))
    441     result = PP_ERROR_FAILED;
    442   *output_handle = IPC::PlatformFileForTransitToPlatformFile(transit_file);
    443 
    444   // End this operation now, so the user's callback can execute another FileIO
    445   // operation, assuming there are no other pending operations.
    446   state_manager_.SetOperationFinished();
    447   callback->Run(result);
    448 }
    449 
    450 }  // namespace proxy
    451 }  // namespace ppapi
    452