• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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().
DummyGetDataBuffer(void * user_data,uint32_t count,uint32_t size)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.
DoClose(base::File auto_close_file)42 void DoClose(base::File auto_close_file) {
43 }
44 
45 }  // namespace
46 
47 namespace ppapi {
48 namespace proxy {
49 
QueryOp(scoped_refptr<FileHolder> file_holder)50 FileIOResource::QueryOp::QueryOp(scoped_refptr<FileHolder> file_holder)
51     : file_holder_(file_holder) {
52   DCHECK(file_holder_.get());
53 }
54 
~QueryOp()55 FileIOResource::QueryOp::~QueryOp() {
56 }
57 
DoWork()58 int32_t FileIOResource::QueryOp::DoWork() {
59   return file_holder_->file()->GetInfo(&file_info_) ? PP_OK : PP_ERROR_FAILED;
60 }
61 
ReadOp(scoped_refptr<FileHolder> file_holder,int64_t offset,int32_t bytes_to_read)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 
~ReadOp()71 FileIOResource::ReadOp::~ReadOp() {
72 }
73 
DoWork()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 
WriteOp(scoped_refptr<FileHolder> file_holder,int64_t offset,scoped_ptr<char[]> buffer,int32_t bytes_to_write,bool append)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 
~WriteOp()92 FileIOResource::WriteOp::~WriteOp() {
93 }
94 
DoWork()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 
FileIOResource(Connection connection,PP_Instance instance)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 
~FileIOResource()117 FileIOResource::~FileIOResource() {
118   Close();
119 }
120 
AsPPB_FileIO_API()121 PPB_FileIO_API* FileIOResource::AsPPB_FileIO_API() {
122   return this;
123 }
124 
Open(PP_Resource file_ref,int32_t open_flags,scoped_refptr<TrackedCallback> callback)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 
Query(PP_FileInfo * info,scoped_refptr<TrackedCallback> callback)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 
Touch(PP_Time last_access_time,PP_Time last_modified_time,scoped_refptr<TrackedCallback> callback)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 
Read(int64_t offset,char * buffer,int32_t bytes_to_read,scoped_refptr<TrackedCallback> callback)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 
ReadToArray(int64_t offset,int32_t max_read_length,PP_ArrayOutput * array_output,scoped_refptr<TrackedCallback> callback)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 
Write(int64_t offset,const char * buffer,int32_t bytes_to_write,scoped_refptr<TrackedCallback> callback)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 
SetLength(int64_t length,scoped_refptr<TrackedCallback> callback)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 
Flush(scoped_refptr<TrackedCallback> callback)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 
GetMaxWrittenOffset() const372 int64_t FileIOResource::GetMaxWrittenOffset() const {
373   return max_written_offset_;
374 }
375 
GetAppendModeWriteAmount() const376 int64_t FileIOResource::GetAppendModeWriteAmount() const {
377   return append_mode_write_amount_;
378 }
379 
SetMaxWrittenOffset(int64_t max_written_offset)380 void FileIOResource::SetMaxWrittenOffset(int64_t max_written_offset) {
381   max_written_offset_ = max_written_offset;
382 }
383 
SetAppendModeWriteAmount(int64_t append_mode_write_amount)384 void FileIOResource::SetAppendModeWriteAmount(
385     int64_t append_mode_write_amount) {
386   append_mode_write_amount_ = append_mode_write_amount;
387 }
388 
Close()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 
RequestOSFileHandle(PP_FileHandle * handle,scoped_refptr<TrackedCallback> callback)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 
FileHolder(PP_FileHandle file_handle)424 FileIOResource::FileHolder::FileHolder(PP_FileHandle file_handle)
425     : file_(file_handle) {
426 }
427 
428 // static
IsValid(const scoped_refptr<FileIOResource::FileHolder> & handle)429 bool FileIOResource::FileHolder::IsValid(
430     const scoped_refptr<FileIOResource::FileHolder>& handle) {
431   return handle.get() && handle->file_.IsValid();
432 }
433 
~FileHolder()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 
ReadValidated(int64_t offset,int32_t bytes_to_read,const PP_ArrayOutput & array_output,scoped_refptr<TrackedCallback> callback)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 
WriteValidated(int64_t offset,const char * buffer,int32_t bytes_to_write,scoped_refptr<TrackedCallback> callback)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 
SetLengthValidated(int64_t length,scoped_refptr<TrackedCallback> callback)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 
OnQueryComplete(scoped_refptr<QueryOp> query_op,PP_FileInfo * info,int32_t result)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 
OnReadComplete(scoped_refptr<ReadOp> read_op,PP_ArrayOutput array_output,int32_t result)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 
OnRequestWriteQuotaComplete(int64_t offset,scoped_ptr<char[]> buffer,int32_t bytes_to_write,scoped_refptr<TrackedCallback> callback,int64_t granted)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 
OnRequestSetLengthQuotaComplete(int64_t length,scoped_refptr<TrackedCallback> callback,int64_t granted)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 
OnWriteComplete(int32_t result)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 
OnPluginMsgGeneralComplete(scoped_refptr<TrackedCallback> callback,const ResourceMessageReplyParams & params)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 
OnPluginMsgOpenFileComplete(scoped_refptr<TrackedCallback> callback,const ResourceMessageReplyParams & params,PP_Resource quota_file_system,int64_t max_written_offset)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 
OnPluginMsgRequestOSFileHandleComplete(scoped_refptr<TrackedCallback> callback,PP_FileHandle * output_handle,const ResourceMessageReplyParams & params)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