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::File auto_close_file) {
     43 }
     44 
     45 }  // namespace
     46 
     47 namespace ppapi {
     48 namespace proxy {
     49 
     50 FileIOResource::QueryOp::QueryOp(scoped_refptr<FileHolder> file_holder)
     51     : file_holder_(file_holder) {
     52   DCHECK(file_holder_.get());
     53 }
     54 
     55 FileIOResource::QueryOp::~QueryOp() {
     56 }
     57 
     58 int32_t FileIOResource::QueryOp::DoWork() {
     59   return file_holder_->file()->GetInfo(&file_info_) ? PP_OK : PP_ERROR_FAILED;
     60 }
     61 
     62 FileIOResource::ReadOp::ReadOp(scoped_refptr<FileHolder> file_holder,
     63                                int64_t offset,
     64                                int32_t bytes_to_read)
     65   : file_holder_(file_holder),
     66     offset_(offset),
     67     bytes_to_read_(bytes_to_read) {
     68   DCHECK(file_holder_.get());
     69 }
     70 
     71 FileIOResource::ReadOp::~ReadOp() {
     72 }
     73 
     74 int32_t FileIOResource::ReadOp::DoWork() {
     75   DCHECK(!buffer_.get());
     76   buffer_.reset(new char[bytes_to_read_]);
     77   return file_holder_->file()->Read(offset_, buffer_.get(), bytes_to_read_);
     78 }
     79 
     80 FileIOResource::WriteOp::WriteOp(scoped_refptr<FileHolder> file_holder,
     81                                  int64_t offset,
     82                                  scoped_ptr<char[]> buffer,
     83                                  int32_t bytes_to_write,
     84                                  bool append)
     85     : file_holder_(file_holder),
     86       offset_(offset),
     87       buffer_(buffer.Pass()),
     88       bytes_to_write_(bytes_to_write),
     89       append_(append) {
     90 }
     91 
     92 FileIOResource::WriteOp::~WriteOp() {
     93 }
     94 
     95 int32_t FileIOResource::WriteOp::DoWork() {
     96   // In append mode, we can't call Write, since NaCl doesn't implement fcntl,
     97   // causing the function to call pwrite, which is incorrect.
     98   if (append_) {
     99     return file_holder_->file()->WriteAtCurrentPos(buffer_.get(),
    100                                                    bytes_to_write_);
    101   } else {
    102     return file_holder_->file()->Write(offset_, buffer_.get(), bytes_to_write_);
    103   }
    104 }
    105 
    106 FileIOResource::FileIOResource(Connection connection, PP_Instance instance)
    107     : PluginResource(connection, instance),
    108       file_system_type_(PP_FILESYSTEMTYPE_INVALID),
    109       open_flags_(0),
    110       max_written_offset_(0),
    111       append_mode_write_amount_(0),
    112       check_quota_(false),
    113       called_close_(false) {
    114   SendCreate(BROWSER, PpapiHostMsg_FileIO_Create());
    115 }
    116 
    117 FileIOResource::~FileIOResource() {
    118   Close();
    119 }
    120 
    121 PPB_FileIO_API* FileIOResource::AsPPB_FileIO_API() {
    122   return this;
    123 }
    124 
    125 int32_t FileIOResource::Open(PP_Resource file_ref,
    126                              int32_t open_flags,
    127                              scoped_refptr<TrackedCallback> callback) {
    128   EnterResourceNoLock<PPB_FileRef_API> enter_file_ref(file_ref, true);
    129   if (enter_file_ref.failed())
    130     return PP_ERROR_BADRESOURCE;
    131 
    132   PPB_FileRef_API* file_ref_api = enter_file_ref.object();
    133   const FileRefCreateInfo& create_info = file_ref_api->GetCreateInfo();
    134   if (!FileSystemTypeIsValid(create_info.file_system_type)) {
    135     NOTREACHED();
    136     return PP_ERROR_FAILED;
    137   }
    138   int32_t rv = state_manager_.CheckOperationState(
    139       FileIOStateManager::OPERATION_EXCLUSIVE, false);
    140   if (rv != PP_OK)
    141     return rv;
    142 
    143   open_flags_ = open_flags;
    144   file_system_type_ = create_info.file_system_type;
    145 
    146   if (create_info.file_system_plugin_resource) {
    147     EnterResourceNoLock<PPB_FileSystem_API> enter_file_system(
    148         create_info.file_system_plugin_resource, true);
    149     if (enter_file_system.failed())
    150       return PP_ERROR_FAILED;
    151     // Take a reference on the FileSystem resource. The FileIO host uses the
    152     // FileSystem host for running tasks and checking quota.
    153     file_system_resource_ = enter_file_system.resource();
    154   }
    155 
    156   // Take a reference on the FileRef resource while we're opening the file; we
    157   // don't want the plugin destroying it during the Open operation.
    158   file_ref_ = enter_file_ref.resource();
    159 
    160   Call<PpapiPluginMsg_FileIO_OpenReply>(BROWSER,
    161       PpapiHostMsg_FileIO_Open(
    162           file_ref,
    163           open_flags),
    164       base::Bind(&FileIOResource::OnPluginMsgOpenFileComplete, this,
    165                  callback));
    166 
    167   state_manager_.SetPendingOperation(FileIOStateManager::OPERATION_EXCLUSIVE);
    168   return PP_OK_COMPLETIONPENDING;
    169 }
    170 
    171 int32_t FileIOResource::Query(PP_FileInfo* info,
    172                               scoped_refptr<TrackedCallback> callback) {
    173   int32_t rv = state_manager_.CheckOperationState(
    174       FileIOStateManager::OPERATION_EXCLUSIVE, true);
    175   if (rv != PP_OK)
    176     return rv;
    177   if (!info)
    178     return PP_ERROR_BADARGUMENT;
    179   if (!FileHolder::IsValid(file_holder_))
    180     return PP_ERROR_FAILED;
    181 
    182   state_manager_.SetPendingOperation(FileIOStateManager::OPERATION_EXCLUSIVE);
    183 
    184   // If the callback is blocking, perform the task on the calling thread.
    185   if (callback->is_blocking()) {
    186     int32_t result = PP_ERROR_FAILED;
    187     base::File::Info file_info;
    188     // The plugin could release its reference to this instance when we release
    189     // the proxy lock below.
    190     scoped_refptr<FileIOResource> protect(this);
    191     {
    192       // Release the proxy lock while making a potentially slow file call.
    193       ProxyAutoUnlock unlock;
    194       if (file_holder_->file()->GetInfo(&file_info))
    195         result = PP_OK;
    196     }
    197     if (result == PP_OK) {
    198       // This writes the file info into the plugin's PP_FileInfo struct.
    199       ppapi::FileInfoToPepperFileInfo(file_info,
    200                                       file_system_type_,
    201                                       info);
    202     }
    203     state_manager_.SetOperationFinished();
    204     return result;
    205   }
    206 
    207   // For the non-blocking case, post a task to the file thread and add a
    208   // completion task to write the result.
    209   scoped_refptr<QueryOp> query_op(new QueryOp(file_holder_));
    210   base::PostTaskAndReplyWithResult(
    211       PpapiGlobals::Get()->GetFileTaskRunner(),
    212       FROM_HERE,
    213       Bind(&FileIOResource::QueryOp::DoWork, query_op),
    214       RunWhileLocked(Bind(&TrackedCallback::Run, callback)));
    215   callback->set_completion_task(
    216       Bind(&FileIOResource::OnQueryComplete, this, query_op, info));
    217 
    218   return PP_OK_COMPLETIONPENDING;
    219 }
    220 
    221 int32_t FileIOResource::Touch(PP_Time last_access_time,
    222                               PP_Time last_modified_time,
    223                               scoped_refptr<TrackedCallback> callback) {
    224   int32_t rv = state_manager_.CheckOperationState(
    225       FileIOStateManager::OPERATION_EXCLUSIVE, true);
    226   if (rv != PP_OK)
    227     return rv;
    228 
    229   Call<PpapiPluginMsg_FileIO_GeneralReply>(BROWSER,
    230       PpapiHostMsg_FileIO_Touch(last_access_time, last_modified_time),
    231       base::Bind(&FileIOResource::OnPluginMsgGeneralComplete, this,
    232                  callback));
    233 
    234   state_manager_.SetPendingOperation(FileIOStateManager::OPERATION_EXCLUSIVE);
    235   return PP_OK_COMPLETIONPENDING;
    236 }
    237 
    238 int32_t FileIOResource::Read(int64_t offset,
    239                              char* buffer,
    240                              int32_t bytes_to_read,
    241                              scoped_refptr<TrackedCallback> callback) {
    242   int32_t rv = state_manager_.CheckOperationState(
    243       FileIOStateManager::OPERATION_READ, true);
    244   if (rv != PP_OK)
    245     return rv;
    246 
    247   PP_ArrayOutput output_adapter;
    248   output_adapter.GetDataBuffer = &DummyGetDataBuffer;
    249   output_adapter.user_data = buffer;
    250   return ReadValidated(offset, bytes_to_read, output_adapter, callback);
    251 }
    252 
    253 int32_t FileIOResource::ReadToArray(int64_t offset,
    254                                     int32_t max_read_length,
    255                                     PP_ArrayOutput* array_output,
    256                                     scoped_refptr<TrackedCallback> callback) {
    257   DCHECK(array_output);
    258   int32_t rv = state_manager_.CheckOperationState(
    259       FileIOStateManager::OPERATION_READ, true);
    260   if (rv != PP_OK)
    261     return rv;
    262 
    263   return ReadValidated(offset, max_read_length, *array_output, callback);
    264 }
    265 
    266 int32_t FileIOResource::Write(int64_t offset,
    267                               const char* buffer,
    268                               int32_t bytes_to_write,
    269                               scoped_refptr<TrackedCallback> callback) {
    270   if (!buffer)
    271     return PP_ERROR_FAILED;
    272   if (offset < 0 || bytes_to_write < 0)
    273     return PP_ERROR_FAILED;
    274   if (!FileHolder::IsValid(file_holder_))
    275     return PP_ERROR_FAILED;
    276 
    277   int32_t rv = state_manager_.CheckOperationState(
    278       FileIOStateManager::OPERATION_WRITE, true);
    279   if (rv != PP_OK)
    280     return rv;
    281 
    282   state_manager_.SetPendingOperation(FileIOStateManager::OPERATION_WRITE);
    283 
    284   if (check_quota_) {
    285     int64_t increase = 0;
    286     uint64_t max_offset = 0;
    287     bool append = (open_flags_ & PP_FILEOPENFLAG_APPEND) != 0;
    288     if (append) {
    289       increase = bytes_to_write;
    290     } else {
    291       uint64_t max_offset = offset + bytes_to_write;
    292       if (max_offset > static_cast<uint64_t>(kint64max))
    293         return PP_ERROR_FAILED;  // amount calculation would overflow.
    294       increase = static_cast<int64_t>(max_offset) - max_written_offset_;
    295     }
    296 
    297     if (increase > 0) {
    298       // Request a quota reservation. This makes the Write asynchronous, so we
    299       // must copy the plugin's buffer.
    300       scoped_ptr<char[]> copy(new char[bytes_to_write]);
    301       memcpy(copy.get(), buffer, bytes_to_write);
    302       int64_t result =
    303           file_system_resource_->AsPPB_FileSystem_API()->RequestQuota(
    304               increase,
    305               base::Bind(&FileIOResource::OnRequestWriteQuotaComplete,
    306                          this,
    307                          offset,
    308                          base::Passed(&copy),
    309                          bytes_to_write,
    310                          callback));
    311       if (result == PP_OK_COMPLETIONPENDING)
    312         return PP_OK_COMPLETIONPENDING;
    313       DCHECK(result == increase);
    314 
    315       if (append)
    316         append_mode_write_amount_ += bytes_to_write;
    317       else
    318         max_written_offset_ = max_offset;
    319     }
    320   }
    321   return WriteValidated(offset, buffer, bytes_to_write, callback);
    322 }
    323 
    324 int32_t FileIOResource::SetLength(int64_t length,
    325                                   scoped_refptr<TrackedCallback> callback) {
    326   int32_t rv = state_manager_.CheckOperationState(
    327       FileIOStateManager::OPERATION_EXCLUSIVE, true);
    328   if (rv != PP_OK)
    329     return rv;
    330   if (length < 0)
    331     return PP_ERROR_FAILED;
    332 
    333   if (check_quota_) {
    334     int64_t increase = length - max_written_offset_;
    335     if (increase > 0) {
    336       int32_t result =
    337           file_system_resource_->AsPPB_FileSystem_API()->RequestQuota(
    338               increase,
    339               base::Bind(&FileIOResource::OnRequestSetLengthQuotaComplete,
    340                          this,
    341                          length, callback));
    342       if (result == PP_OK_COMPLETIONPENDING) {
    343         state_manager_.SetPendingOperation(
    344             FileIOStateManager::OPERATION_EXCLUSIVE);
    345         return PP_OK_COMPLETIONPENDING;
    346       }
    347       DCHECK(result == increase);
    348       max_written_offset_ = length;
    349     }
    350   }
    351 
    352   state_manager_.SetPendingOperation(FileIOStateManager::OPERATION_EXCLUSIVE);
    353   SetLengthValidated(length, callback);
    354   return PP_OK_COMPLETIONPENDING;
    355 }
    356 
    357 int32_t FileIOResource::Flush(scoped_refptr<TrackedCallback> callback) {
    358   int32_t rv = state_manager_.CheckOperationState(
    359       FileIOStateManager::OPERATION_EXCLUSIVE, true);
    360   if (rv != PP_OK)
    361     return rv;
    362 
    363   Call<PpapiPluginMsg_FileIO_GeneralReply>(BROWSER,
    364       PpapiHostMsg_FileIO_Flush(),
    365       base::Bind(&FileIOResource::OnPluginMsgGeneralComplete, this,
    366                  callback));
    367 
    368   state_manager_.SetPendingOperation(FileIOStateManager::OPERATION_EXCLUSIVE);
    369   return PP_OK_COMPLETIONPENDING;
    370 }
    371 
    372 int64_t FileIOResource::GetMaxWrittenOffset() const {
    373   return max_written_offset_;
    374 }
    375 
    376 int64_t FileIOResource::GetAppendModeWriteAmount() const {
    377   return append_mode_write_amount_;
    378 }
    379 
    380 void FileIOResource::SetMaxWrittenOffset(int64_t max_written_offset) {
    381   max_written_offset_ = max_written_offset;
    382 }
    383 
    384 void FileIOResource::SetAppendModeWriteAmount(
    385     int64_t append_mode_write_amount) {
    386   append_mode_write_amount_ = append_mode_write_amount;
    387 }
    388 
    389 void FileIOResource::Close() {
    390   if (called_close_)
    391     return;
    392 
    393   called_close_ = true;
    394   if (check_quota_) {
    395     check_quota_ = false;
    396     file_system_resource_->AsPPB_FileSystem_API()->CloseQuotaFile(
    397         pp_resource());
    398   }
    399 
    400   if (file_holder_.get())
    401     file_holder_ = NULL;
    402 
    403   Post(BROWSER, PpapiHostMsg_FileIO_Close(
    404       FileGrowth(max_written_offset_, append_mode_write_amount_)));
    405 }
    406 
    407 int32_t FileIOResource::RequestOSFileHandle(
    408     PP_FileHandle* handle,
    409     scoped_refptr<TrackedCallback> callback) {
    410   int32_t rv = state_manager_.CheckOperationState(
    411       FileIOStateManager::OPERATION_EXCLUSIVE, true);
    412   if (rv != PP_OK)
    413     return rv;
    414 
    415   Call<PpapiPluginMsg_FileIO_RequestOSFileHandleReply>(BROWSER,
    416       PpapiHostMsg_FileIO_RequestOSFileHandle(),
    417       base::Bind(&FileIOResource::OnPluginMsgRequestOSFileHandleComplete, this,
    418                  callback, handle));
    419 
    420   state_manager_.SetPendingOperation(FileIOStateManager::OPERATION_EXCLUSIVE);
    421   return PP_OK_COMPLETIONPENDING;
    422 }
    423 
    424 FileIOResource::FileHolder::FileHolder(PP_FileHandle file_handle)
    425     : file_(file_handle) {
    426 }
    427 
    428 // static
    429 bool FileIOResource::FileHolder::IsValid(
    430     const scoped_refptr<FileIOResource::FileHolder>& handle) {
    431   return handle.get() && handle->file_.IsValid();
    432 }
    433 
    434 FileIOResource::FileHolder::~FileHolder() {
    435   if (file_.IsValid()) {
    436     base::TaskRunner* file_task_runner =
    437         PpapiGlobals::Get()->GetFileTaskRunner();
    438     file_task_runner->PostTask(FROM_HERE,
    439                                base::Bind(&DoClose, Passed(&file_)));
    440   }
    441 }
    442 
    443 int32_t FileIOResource::ReadValidated(int64_t offset,
    444                                       int32_t bytes_to_read,
    445                                       const PP_ArrayOutput& array_output,
    446                                       scoped_refptr<TrackedCallback> callback) {
    447   if (bytes_to_read < 0)
    448     return PP_ERROR_FAILED;
    449   if (!FileHolder::IsValid(file_holder_))
    450     return PP_ERROR_FAILED;
    451 
    452   state_manager_.SetPendingOperation(FileIOStateManager::OPERATION_READ);
    453 
    454   bytes_to_read = std::min(bytes_to_read, kMaxReadWriteSize);
    455   if (callback->is_blocking()) {
    456     char* buffer = static_cast<char*>(
    457         array_output.GetDataBuffer(array_output.user_data, bytes_to_read, 1));
    458     int32_t result = PP_ERROR_FAILED;
    459     // The plugin could release its reference to this instance when we release
    460     // the proxy lock below.
    461     scoped_refptr<FileIOResource> protect(this);
    462     if (buffer) {
    463       // Release the proxy lock while making a potentially slow file call.
    464       ProxyAutoUnlock unlock;
    465       result = file_holder_->file()->Read(offset, buffer, bytes_to_read);
    466       if (result < 0)
    467         result = PP_ERROR_FAILED;
    468     }
    469     state_manager_.SetOperationFinished();
    470     return result;
    471   }
    472 
    473   // For the non-blocking case, post a task to the file thread.
    474   scoped_refptr<ReadOp> read_op(
    475       new ReadOp(file_holder_, offset, bytes_to_read));
    476   base::PostTaskAndReplyWithResult(
    477       PpapiGlobals::Get()->GetFileTaskRunner(),
    478       FROM_HERE,
    479       Bind(&FileIOResource::ReadOp::DoWork, read_op),
    480       RunWhileLocked(Bind(&TrackedCallback::Run, callback)));
    481   callback->set_completion_task(
    482       Bind(&FileIOResource::OnReadComplete, this, read_op, array_output));
    483 
    484   return PP_OK_COMPLETIONPENDING;
    485 }
    486 
    487 int32_t FileIOResource::WriteValidated(
    488     int64_t offset,
    489     const char* buffer,
    490     int32_t bytes_to_write,
    491     scoped_refptr<TrackedCallback> callback) {
    492   bool append = (open_flags_ & PP_FILEOPENFLAG_APPEND) != 0;
    493   if (callback->is_blocking()) {
    494     int32_t result;
    495     {
    496       // Release the proxy lock while making a potentially slow file call.
    497       ProxyAutoUnlock unlock;
    498       if (append) {
    499         result = file_holder_->file()->WriteAtCurrentPos(buffer,
    500                                                          bytes_to_write);
    501       } else {
    502         result = file_holder_->file()->Write(offset, buffer, bytes_to_write);
    503       }
    504     }
    505     if (result < 0)
    506       result = PP_ERROR_FAILED;
    507 
    508     state_manager_.SetOperationFinished();
    509     return result;
    510   }
    511 
    512   // For the non-blocking case, post a task to the file thread. We must copy the
    513   // plugin's buffer at this point.
    514   scoped_ptr<char[]> copy(new char[bytes_to_write]);
    515   memcpy(copy.get(), buffer, bytes_to_write);
    516   scoped_refptr<WriteOp> write_op(
    517       new WriteOp(file_holder_, offset, copy.Pass(), bytes_to_write, append));
    518   base::PostTaskAndReplyWithResult(
    519       PpapiGlobals::Get()->GetFileTaskRunner(),
    520       FROM_HERE,
    521       Bind(&FileIOResource::WriteOp::DoWork, write_op),
    522       RunWhileLocked(Bind(&TrackedCallback::Run, callback)));
    523   callback->set_completion_task(Bind(&FileIOResource::OnWriteComplete, this));
    524 
    525   return PP_OK_COMPLETIONPENDING;
    526 }
    527 
    528 void FileIOResource::SetLengthValidated(
    529     int64_t length,
    530     scoped_refptr<TrackedCallback> callback) {
    531   Call<PpapiPluginMsg_FileIO_GeneralReply>(BROWSER,
    532       PpapiHostMsg_FileIO_SetLength(length),
    533       base::Bind(&FileIOResource::OnPluginMsgGeneralComplete, this,
    534                  callback));
    535 
    536   // On the browser side we grow |max_written_offset_| monotonically, due to the
    537   // unpredictable ordering of plugin side Write and SetLength calls. Match that
    538   // behavior here.
    539   if (max_written_offset_ < length)
    540     max_written_offset_ = length;
    541 }
    542 
    543 int32_t FileIOResource::OnQueryComplete(scoped_refptr<QueryOp> query_op,
    544                                         PP_FileInfo* info,
    545                                         int32_t result) {
    546   DCHECK(state_manager_.get_pending_operation() ==
    547          FileIOStateManager::OPERATION_EXCLUSIVE);
    548 
    549   if (result == PP_OK) {
    550     // This writes the file info into the plugin's PP_FileInfo struct.
    551     ppapi::FileInfoToPepperFileInfo(query_op->file_info(),
    552                                     file_system_type_,
    553                                     info);
    554   }
    555   state_manager_.SetOperationFinished();
    556   return result;
    557 }
    558 
    559 int32_t FileIOResource::OnReadComplete(scoped_refptr<ReadOp> read_op,
    560                                        PP_ArrayOutput array_output,
    561                                        int32_t result) {
    562   DCHECK(state_manager_.get_pending_operation() ==
    563          FileIOStateManager::OPERATION_READ);
    564   if (result >= 0) {
    565     ArrayWriter output;
    566     output.set_pp_array_output(array_output);
    567     if (output.is_valid())
    568       output.StoreArray(read_op->buffer(), result);
    569     else
    570       result = PP_ERROR_FAILED;
    571   } else {
    572     // The read operation failed.
    573     result = PP_ERROR_FAILED;
    574   }
    575   state_manager_.SetOperationFinished();
    576   return result;
    577 }
    578 
    579 void FileIOResource::OnRequestWriteQuotaComplete(
    580     int64_t offset,
    581     scoped_ptr<char[]> buffer,
    582     int32_t bytes_to_write,
    583     scoped_refptr<TrackedCallback> callback,
    584     int64_t granted) {
    585   DCHECK(granted >= 0);
    586   if (granted == 0) {
    587     callback->Run(PP_ERROR_NOQUOTA);
    588     return;
    589   }
    590   if (open_flags_ & PP_FILEOPENFLAG_APPEND) {
    591     DCHECK_LE(bytes_to_write, granted);
    592     append_mode_write_amount_ += bytes_to_write;
    593   } else {
    594     DCHECK_LE(offset + bytes_to_write - max_written_offset_, granted);
    595 
    596     int64_t max_offset = offset + bytes_to_write;
    597     if (max_written_offset_ < max_offset)
    598       max_written_offset_ = max_offset;
    599   }
    600 
    601   if (callback->is_blocking()) {
    602     int32_t result =
    603         WriteValidated(offset, buffer.get(), bytes_to_write, callback);
    604     DCHECK(result != PP_OK_COMPLETIONPENDING);
    605     callback->Run(result);
    606   } else {
    607     bool append = (open_flags_ & PP_FILEOPENFLAG_APPEND) != 0;
    608     scoped_refptr<WriteOp> write_op(new WriteOp(
    609         file_holder_, offset, buffer.Pass(), bytes_to_write, append));
    610     base::PostTaskAndReplyWithResult(
    611         PpapiGlobals::Get()->GetFileTaskRunner(),
    612         FROM_HERE,
    613         Bind(&FileIOResource::WriteOp::DoWork, write_op),
    614         RunWhileLocked(Bind(&TrackedCallback::Run, callback)));
    615     callback->set_completion_task(Bind(&FileIOResource::OnWriteComplete, this));
    616   }
    617 }
    618 
    619 void FileIOResource::OnRequestSetLengthQuotaComplete(
    620     int64_t length,
    621     scoped_refptr<TrackedCallback> callback,
    622     int64_t granted) {
    623   DCHECK(granted >= 0);
    624   if (granted == 0) {
    625     callback->Run(PP_ERROR_NOQUOTA);
    626     return;
    627   }
    628 
    629   DCHECK_LE(length - max_written_offset_, granted);
    630   if (max_written_offset_ < length)
    631     max_written_offset_ = length;
    632   SetLengthValidated(length, callback);
    633 }
    634 
    635 int32_t FileIOResource::OnWriteComplete(int32_t result) {
    636   DCHECK(state_manager_.get_pending_operation() ==
    637          FileIOStateManager::OPERATION_WRITE);
    638   // |result| is the return value of WritePlatformFile; -1 indicates failure.
    639   if (result < 0)
    640     result = PP_ERROR_FAILED;
    641 
    642   state_manager_.SetOperationFinished();
    643   return result;
    644 }
    645 
    646 void FileIOResource::OnPluginMsgGeneralComplete(
    647     scoped_refptr<TrackedCallback> callback,
    648     const ResourceMessageReplyParams& params) {
    649   DCHECK(state_manager_.get_pending_operation() ==
    650          FileIOStateManager::OPERATION_EXCLUSIVE ||
    651          state_manager_.get_pending_operation() ==
    652          FileIOStateManager::OPERATION_WRITE);
    653   // End this operation now, so the user's callback can execute another FileIO
    654   // operation, assuming there are no other pending operations.
    655   state_manager_.SetOperationFinished();
    656   callback->Run(params.result());
    657 }
    658 
    659 void FileIOResource::OnPluginMsgOpenFileComplete(
    660     scoped_refptr<TrackedCallback> callback,
    661     const ResourceMessageReplyParams& params,
    662     PP_Resource quota_file_system,
    663     int64_t max_written_offset) {
    664   DCHECK(state_manager_.get_pending_operation() ==
    665          FileIOStateManager::OPERATION_EXCLUSIVE);
    666 
    667   // Release the FileRef resource.
    668   file_ref_ = NULL;
    669   int32_t result = params.result();
    670   if (result == PP_OK) {
    671     state_manager_.SetOpenSucceed();
    672 
    673     if (quota_file_system) {
    674       DCHECK(quota_file_system == file_system_resource_->pp_resource());
    675       check_quota_ = true;
    676       max_written_offset_ = max_written_offset;
    677       file_system_resource_->AsPPB_FileSystem_API()->OpenQuotaFile(
    678           pp_resource());
    679     }
    680 
    681     IPC::PlatformFileForTransit transit_file;
    682     if (params.TakeFileHandleAtIndex(0, &transit_file)) {
    683       file_holder_ = new FileHolder(
    684           IPC::PlatformFileForTransitToPlatformFile(transit_file));
    685     }
    686   }
    687   // End this operation now, so the user's callback can execute another FileIO
    688   // operation, assuming there are no other pending operations.
    689   state_manager_.SetOperationFinished();
    690   callback->Run(result);
    691 }
    692 
    693 void FileIOResource::OnPluginMsgRequestOSFileHandleComplete(
    694     scoped_refptr<TrackedCallback> callback,
    695     PP_FileHandle* output_handle,
    696     const ResourceMessageReplyParams& params) {
    697   DCHECK(state_manager_.get_pending_operation() ==
    698          FileIOStateManager::OPERATION_EXCLUSIVE);
    699 
    700   if (!TrackedCallback::IsPending(callback)) {
    701     state_manager_.SetOperationFinished();
    702     return;
    703   }
    704 
    705   int32_t result = params.result();
    706   IPC::PlatformFileForTransit transit_file;
    707   if (!params.TakeFileHandleAtIndex(0, &transit_file))
    708     result = PP_ERROR_FAILED;
    709   *output_handle = IPC::PlatformFileForTransitToPlatformFile(transit_file);
    710 
    711   // End this operation now, so the user's callback can execute another FileIO
    712   // operation, assuming there are no other pending operations.
    713   state_manager_.SetOperationFinished();
    714   callback->Run(result);
    715 }
    716 
    717 }  // namespace proxy
    718 }  // namespace ppapi
    719