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