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(©),
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