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