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
23using ppapi::thunk::EnterResourceNoLock;
24using ppapi::thunk::PPB_FileIO_API;
25using ppapi::thunk::PPB_FileRef_API;
26using ppapi::thunk::PPB_FileSystem_API;
27
28namespace {
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.
34static const int32_t kMaxReadWriteSize = 32 * 1024 * 1024;  // 32MB
35
36// An adapter to let Read() share the same implementation with ReadToArray().
37void* 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.
42void DoClose(base::File auto_close_file) {
43}
44
45}  // namespace
46
47namespace ppapi {
48namespace proxy {
49
50FileIOResource::QueryOp::QueryOp(scoped_refptr<FileHolder> file_holder)
51    : file_holder_(file_holder) {
52  DCHECK(file_holder_.get());
53}
54
55FileIOResource::QueryOp::~QueryOp() {
56}
57
58int32_t FileIOResource::QueryOp::DoWork() {
59  return file_holder_->file()->GetInfo(&file_info_) ? PP_OK : PP_ERROR_FAILED;
60}
61
62FileIOResource::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
71FileIOResource::ReadOp::~ReadOp() {
72}
73
74int32_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
80FileIOResource::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
92FileIOResource::WriteOp::~WriteOp() {
93}
94
95int32_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
106FileIOResource::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
117FileIOResource::~FileIOResource() {
118  Close();
119}
120
121PPB_FileIO_API* FileIOResource::AsPPB_FileIO_API() {
122  return this;
123}
124
125int32_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
171int32_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
221int32_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
238int32_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
253int32_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
266int32_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
324int32_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
357int32_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
372int64_t FileIOResource::GetMaxWrittenOffset() const {
373  return max_written_offset_;
374}
375
376int64_t FileIOResource::GetAppendModeWriteAmount() const {
377  return append_mode_write_amount_;
378}
379
380void FileIOResource::SetMaxWrittenOffset(int64_t max_written_offset) {
381  max_written_offset_ = max_written_offset;
382}
383
384void FileIOResource::SetAppendModeWriteAmount(
385    int64_t append_mode_write_amount) {
386  append_mode_write_amount_ = append_mode_write_amount;
387}
388
389void 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
407int32_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
424FileIOResource::FileHolder::FileHolder(PP_FileHandle file_handle)
425    : file_(file_handle) {
426}
427
428// static
429bool FileIOResource::FileHolder::IsValid(
430    const scoped_refptr<FileIOResource::FileHolder>& handle) {
431  return handle.get() && handle->file_.IsValid();
432}
433
434FileIOResource::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
443int32_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
487int32_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
528void 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
543int32_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
559int32_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
579void 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
619void 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
635int32_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
646void 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
659void 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
693void 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