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