blob: 347d4ef89168fd5102ef4cf8184e427155c58925 [file] [log] [blame]
[email protected]c77144722013-01-19 04:16:361// 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"
[email protected]914a4ba92013-08-10 13:38:488#include "base/task_runner_util.h"
[email protected]c77144722013-01-19 04:16:369#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"
[email protected]d45f29b2013-08-09 04:18:0613#include "ppapi/shared_impl/file_type_conversion.h"
[email protected]c77144722013-01-19 04:16:3614#include "ppapi/shared_impl/ppapi_globals.h"
[email protected]d45f29b2013-08-09 04:18:0615#include "ppapi/shared_impl/proxy_lock.h"
[email protected]c77144722013-01-19 04:16:3616#include "ppapi/shared_impl/resource_tracker.h"
17#include "ppapi/thunk/enter.h"
18#include "ppapi/thunk/ppb_file_ref_api.h"
19
20using ppapi::thunk::EnterResourceNoLock;
21using ppapi::thunk::PPB_FileIO_API;
22using ppapi::thunk::PPB_FileRef_API;
23
24namespace {
25
[email protected]914a4ba92013-08-10 13:38:4826// We must allocate a buffer sized according to the request of the plugin. To
27// reduce the chance of out-of-memory errors, we cap the read size to 32MB.
28// This is OK since the API specifies that it may perform a partial read.
29static const int32_t kMaxReadSize = 32 * 1024 * 1024; // 32MB
30
[email protected]c77144722013-01-19 04:16:3631// An adapter to let Read() share the same implementation with ReadToArray().
32void* DummyGetDataBuffer(void* user_data, uint32_t count, uint32_t size) {
33 return user_data;
34}
35
[email protected]d45f29b2013-08-09 04:18:0636// File thread task to close the file handle.
37void DoClose(base::PlatformFile file) {
38 base::ClosePlatformFile(file);
39}
40
[email protected]c77144722013-01-19 04:16:3641} // namespace
42
43namespace ppapi {
44namespace proxy {
45
[email protected]d35d3f42013-10-02 19:52:2046FileIOResource::QueryOp::QueryOp(scoped_refptr<FileHandleHolder> file_handle)
[email protected]914a4ba92013-08-10 13:38:4847 : file_handle_(file_handle) {
[email protected]d35d3f42013-10-02 19:52:2048 DCHECK(file_handle_);
[email protected]914a4ba92013-08-10 13:38:4849}
50
51FileIOResource::QueryOp::~QueryOp() {
52}
53
54int32_t FileIOResource::QueryOp::DoWork() {
[email protected]d35d3f42013-10-02 19:52:2055 return base::GetPlatformFileInfo(file_handle_->raw_handle(), &file_info_) ?
[email protected]914a4ba92013-08-10 13:38:4856 PP_OK : PP_ERROR_FAILED;
57}
58
[email protected]d35d3f42013-10-02 19:52:2059FileIOResource::ReadOp::ReadOp(scoped_refptr<FileHandleHolder> file_handle,
[email protected]914a4ba92013-08-10 13:38:4860 int64_t offset,
61 int32_t bytes_to_read)
62 : file_handle_(file_handle),
63 offset_(offset),
64 bytes_to_read_(bytes_to_read) {
[email protected]d35d3f42013-10-02 19:52:2065 DCHECK(file_handle_);
[email protected]914a4ba92013-08-10 13:38:4866}
67
68FileIOResource::ReadOp::~ReadOp() {
69}
70
71int32_t FileIOResource::ReadOp::DoWork() {
72 DCHECK(!buffer_.get());
73 buffer_.reset(new char[bytes_to_read_]);
74 return base::ReadPlatformFile(
[email protected]d35d3f42013-10-02 19:52:2075 file_handle_->raw_handle(), offset_, buffer_.get(), bytes_to_read_);
[email protected]914a4ba92013-08-10 13:38:4876}
77
[email protected]c77144722013-01-19 04:16:3678FileIOResource::FileIOResource(Connection connection, PP_Instance instance)
[email protected]d45f29b2013-08-09 04:18:0679 : PluginResource(connection, instance),
[email protected]d45f29b2013-08-09 04:18:0680 file_system_type_(PP_FILESYSTEMTYPE_INVALID) {
[email protected]c77144722013-01-19 04:16:3681 SendCreate(RENDERER, PpapiHostMsg_FileIO_Create());
82}
83
84FileIOResource::~FileIOResource() {
85}
86
87PPB_FileIO_API* FileIOResource::AsPPB_FileIO_API() {
88 return this;
89}
90
91int32_t FileIOResource::Open(PP_Resource file_ref,
92 int32_t open_flags,
93 scoped_refptr<TrackedCallback> callback) {
94 EnterResourceNoLock<PPB_FileRef_API> enter(file_ref, true);
95 if (enter.failed())
96 return PP_ERROR_BADRESOURCE;
97
[email protected]d45f29b2013-08-09 04:18:0698 PPB_FileRef_API* file_ref_api = enter.object();
99 PP_FileSystemType type = file_ref_api->GetFileSystemType();
100 if (type != PP_FILESYSTEMTYPE_LOCALPERSISTENT &&
101 type != PP_FILESYSTEMTYPE_LOCALTEMPORARY &&
102 type != PP_FILESYSTEMTYPE_EXTERNAL &&
103 type != PP_FILESYSTEMTYPE_ISOLATED) {
104 NOTREACHED();
105 return PP_ERROR_FAILED;
106 }
107 file_system_type_ = type;
108
[email protected]c77144722013-01-19 04:16:36109 int32_t rv = state_manager_.CheckOperationState(
110 FileIOStateManager::OPERATION_EXCLUSIVE, false);
111 if (rv != PP_OK)
112 return rv;
113
[email protected]c6420f082013-09-18 22:42:41114 // Take a reference on the FileRef resource while we're opening the file; we
115 // don't want the plugin destroying it during the Open operation.
116 file_ref_ = enter.resource();
117
[email protected]c77144722013-01-19 04:16:36118 Call<PpapiPluginMsg_FileIO_OpenReply>(RENDERER,
119 PpapiHostMsg_FileIO_Open(
[email protected]c6420f082013-09-18 22:42:41120 file_ref,
[email protected]c77144722013-01-19 04:16:36121 open_flags),
122 base::Bind(&FileIOResource::OnPluginMsgOpenFileComplete, this,
123 callback));
124
125 state_manager_.SetPendingOperation(FileIOStateManager::OPERATION_EXCLUSIVE);
126 return PP_OK_COMPLETIONPENDING;
127}
128
129int32_t FileIOResource::Query(PP_FileInfo* info,
130 scoped_refptr<TrackedCallback> callback) {
131 int32_t rv = state_manager_.CheckOperationState(
132 FileIOStateManager::OPERATION_EXCLUSIVE, true);
133 if (rv != PP_OK)
134 return rv;
[email protected]914a4ba92013-08-10 13:38:48135 if (!info)
136 return PP_ERROR_BADARGUMENT;
[email protected]d35d3f42013-10-02 19:52:20137 if (!FileHandleHolder::IsValid(file_handle_))
[email protected]914a4ba92013-08-10 13:38:48138 return PP_ERROR_FAILED;
[email protected]c77144722013-01-19 04:16:36139
140 state_manager_.SetPendingOperation(FileIOStateManager::OPERATION_EXCLUSIVE);
[email protected]914a4ba92013-08-10 13:38:48141
142 // If the callback is blocking, perform the task on the calling thread.
143 if (callback->is_blocking()) {
[email protected]452fb142013-10-23 01:49:43144 int32_t result = PP_ERROR_FAILED;
145 base::PlatformFileInfo file_info;
146 // The plugin could release its reference to this instance when we release
147 // the proxy lock below.
148 scoped_refptr<FileIOResource> protect(this);
[email protected]914a4ba92013-08-10 13:38:48149 {
150 // Release the proxy lock while making a potentially slow file call.
151 ProxyAutoUnlock unlock;
[email protected]452fb142013-10-23 01:49:43152 if (base::GetPlatformFileInfo(file_handle_->raw_handle(), &file_info))
153 result = PP_OK;
[email protected]914a4ba92013-08-10 13:38:48154 }
[email protected]452fb142013-10-23 01:49:43155 if (result == PP_OK) {
156 // This writes the file info into the plugin's PP_FileInfo struct.
157 ppapi::PlatformFileInfoToPepperFileInfo(file_info,
158 file_system_type_,
159 info);
160 }
161 state_manager_.SetOperationFinished();
162 return result;
[email protected]914a4ba92013-08-10 13:38:48163 }
164
165 // For the non-blocking case, post a task to the file thread and add a
166 // completion task to write the result.
[email protected]452fb142013-10-23 01:49:43167 scoped_refptr<QueryOp> query_op(new QueryOp(file_handle_));
[email protected]914a4ba92013-08-10 13:38:48168 base::PostTaskAndReplyWithResult(
[email protected]3b5c68582013-10-01 06:20:44169 PpapiGlobals::Get()->GetFileTaskRunner(),
[email protected]914a4ba92013-08-10 13:38:48170 FROM_HERE,
171 Bind(&FileIOResource::QueryOp::DoWork, query_op),
172 RunWhileLocked(Bind(&TrackedCallback::Run, callback)));
173 callback->set_completion_task(
174 Bind(&FileIOResource::OnQueryComplete, this, query_op, info));
175
[email protected]c77144722013-01-19 04:16:36176 return PP_OK_COMPLETIONPENDING;
177}
178
179int32_t FileIOResource::Touch(PP_Time last_access_time,
180 PP_Time last_modified_time,
181 scoped_refptr<TrackedCallback> callback) {
182 int32_t rv = state_manager_.CheckOperationState(
183 FileIOStateManager::OPERATION_EXCLUSIVE, true);
184 if (rv != PP_OK)
185 return rv;
186
187 Call<PpapiPluginMsg_FileIO_GeneralReply>(RENDERER,
188 PpapiHostMsg_FileIO_Touch(last_access_time, last_modified_time),
189 base::Bind(&FileIOResource::OnPluginMsgGeneralComplete, this,
190 callback));
191
192 state_manager_.SetPendingOperation(FileIOStateManager::OPERATION_EXCLUSIVE);
193 return PP_OK_COMPLETIONPENDING;
194}
195
196int32_t FileIOResource::Read(int64_t offset,
197 char* buffer,
198 int32_t bytes_to_read,
199 scoped_refptr<TrackedCallback> callback) {
200 int32_t rv = state_manager_.CheckOperationState(
201 FileIOStateManager::OPERATION_READ, true);
202 if (rv != PP_OK)
203 return rv;
204
205 PP_ArrayOutput output_adapter;
206 output_adapter.GetDataBuffer = &DummyGetDataBuffer;
207 output_adapter.user_data = buffer;
[email protected]c77144722013-01-19 04:16:36208 return ReadValidated(offset, bytes_to_read, output_adapter, callback);
209}
210
211int32_t FileIOResource::ReadToArray(int64_t offset,
212 int32_t max_read_length,
213 PP_ArrayOutput* array_output,
214 scoped_refptr<TrackedCallback> callback) {
215 DCHECK(array_output);
216 int32_t rv = state_manager_.CheckOperationState(
217 FileIOStateManager::OPERATION_READ, true);
218 if (rv != PP_OK)
219 return rv;
220
[email protected]c77144722013-01-19 04:16:36221 return ReadValidated(offset, max_read_length, *array_output, callback);
222}
223
224int32_t FileIOResource::Write(int64_t offset,
225 const char* buffer,
226 int32_t bytes_to_write,
227 scoped_refptr<TrackedCallback> callback) {
228 int32_t rv = state_manager_.CheckOperationState(
229 FileIOStateManager::OPERATION_WRITE, true);
230 if (rv != PP_OK)
231 return rv;
232
233 // TODO(brettw) it would be nice to use a shared memory buffer for large
234 // writes rather than having to copy to a string (which will involve a number
235 // of extra copies to serialize over IPC).
236 Call<PpapiPluginMsg_FileIO_GeneralReply>(RENDERER,
237 PpapiHostMsg_FileIO_Write(offset, std::string(buffer, bytes_to_write)),
238 base::Bind(&FileIOResource::OnPluginMsgGeneralComplete, this,
239 callback));
240
241 state_manager_.SetPendingOperation(FileIOStateManager::OPERATION_WRITE);
242 return PP_OK_COMPLETIONPENDING;
243}
244
245int32_t FileIOResource::SetLength(int64_t length,
246 scoped_refptr<TrackedCallback> callback) {
247 int32_t rv = state_manager_.CheckOperationState(
248 FileIOStateManager::OPERATION_EXCLUSIVE, true);
249 if (rv != PP_OK)
250 return rv;
251
252 Call<PpapiPluginMsg_FileIO_GeneralReply>(RENDERER,
253 PpapiHostMsg_FileIO_SetLength(length),
254 base::Bind(&FileIOResource::OnPluginMsgGeneralComplete, this,
255 callback));
256
257 state_manager_.SetPendingOperation(FileIOStateManager::OPERATION_EXCLUSIVE);
258 return PP_OK_COMPLETIONPENDING;
259}
260
261int32_t FileIOResource::Flush(scoped_refptr<TrackedCallback> callback) {
262 int32_t rv = state_manager_.CheckOperationState(
263 FileIOStateManager::OPERATION_EXCLUSIVE, true);
264 if (rv != PP_OK)
265 return rv;
266
267 Call<PpapiPluginMsg_FileIO_GeneralReply>(RENDERER,
268 PpapiHostMsg_FileIO_Flush(),
269 base::Bind(&FileIOResource::OnPluginMsgGeneralComplete, this,
270 callback));
271
272 state_manager_.SetPendingOperation(FileIOStateManager::OPERATION_EXCLUSIVE);
273 return PP_OK_COMPLETIONPENDING;
274}
275
276void FileIOResource::Close() {
[email protected]d35d3f42013-10-02 19:52:20277 if (file_handle_) {
278 file_handle_ = NULL;
279 }
[email protected]c77144722013-01-19 04:16:36280 Post(RENDERER, PpapiHostMsg_FileIO_Close());
281}
282
[email protected]8f96cef2013-04-01 16:51:13283int32_t FileIOResource::RequestOSFileHandle(
284 PP_FileHandle* handle,
285 scoped_refptr<TrackedCallback> callback) {
286 int32_t rv = state_manager_.CheckOperationState(
287 FileIOStateManager::OPERATION_EXCLUSIVE, true);
288 if (rv != PP_OK)
289 return rv;
290
291 Call<PpapiPluginMsg_FileIO_RequestOSFileHandleReply>(RENDERER,
292 PpapiHostMsg_FileIO_RequestOSFileHandle(),
293 base::Bind(&FileIOResource::OnPluginMsgRequestOSFileHandleComplete, this,
294 callback, handle));
295
296 state_manager_.SetPendingOperation(FileIOStateManager::OPERATION_EXCLUSIVE);
297 return PP_OK_COMPLETIONPENDING;
298}
299
[email protected]d35d3f42013-10-02 19:52:20300FileIOResource::FileHandleHolder::FileHandleHolder(PP_FileHandle file_handle)
301 : raw_handle_(file_handle) {
302}
303
304// static
305bool FileIOResource::FileHandleHolder::IsValid(
306 const scoped_refptr<FileIOResource::FileHandleHolder>& handle) {
307 return handle && (handle->raw_handle() != base::kInvalidPlatformFileValue);
308}
309
310FileIOResource::FileHandleHolder::~FileHandleHolder() {
311 if (raw_handle_ != base::kInvalidPlatformFileValue) {
312 base::TaskRunner* file_task_runner =
313 PpapiGlobals::Get()->GetFileTaskRunner();
314 file_task_runner->PostTask(FROM_HERE,
315 base::Bind(&DoClose, raw_handle_));
316 }
317}
318
[email protected]d45f29b2013-08-09 04:18:06319int32_t FileIOResource::ReadValidated(int64_t offset,
320 int32_t bytes_to_read,
321 const PP_ArrayOutput& array_output,
322 scoped_refptr<TrackedCallback> callback) {
[email protected]914a4ba92013-08-10 13:38:48323 if (bytes_to_read < 0)
324 return PP_ERROR_FAILED;
[email protected]d35d3f42013-10-02 19:52:20325 if (!FileHandleHolder::IsValid(file_handle_))
[email protected]914a4ba92013-08-10 13:38:48326 return PP_ERROR_FAILED;
327
[email protected]d45f29b2013-08-09 04:18:06328 state_manager_.SetPendingOperation(FileIOStateManager::OPERATION_READ);
329
[email protected]914a4ba92013-08-10 13:38:48330 bytes_to_read = std::min(bytes_to_read, kMaxReadSize);
[email protected]914a4ba92013-08-10 13:38:48331 if (callback->is_blocking()) {
[email protected]452fb142013-10-23 01:49:43332 char* buffer = static_cast<char*>(
333 array_output.GetDataBuffer(array_output.user_data, bytes_to_read, 1));
334 int32_t result = PP_ERROR_FAILED;
335 // The plugin could release its reference to this instance when we release
336 // the proxy lock below.
337 scoped_refptr<FileIOResource> protect(this);
338 if (buffer) {
[email protected]914a4ba92013-08-10 13:38:48339 // Release the proxy lock while making a potentially slow file call.
340 ProxyAutoUnlock unlock;
[email protected]452fb142013-10-23 01:49:43341 result = base::ReadPlatformFile(
342 file_handle_->raw_handle(), offset, buffer, bytes_to_read);
343 if (result < 0)
344 result = PP_ERROR_FAILED;
[email protected]d45f29b2013-08-09 04:18:06345 }
[email protected]452fb142013-10-23 01:49:43346 state_manager_.SetOperationFinished();
347 return result;
[email protected]d45f29b2013-08-09 04:18:06348 }
349
[email protected]914a4ba92013-08-10 13:38:48350 // For the non-blocking case, post a task to the file thread.
[email protected]452fb142013-10-23 01:49:43351 scoped_refptr<ReadOp> read_op(
352 new ReadOp(file_handle_, offset, bytes_to_read));
[email protected]914a4ba92013-08-10 13:38:48353 base::PostTaskAndReplyWithResult(
[email protected]3b5c68582013-10-01 06:20:44354 PpapiGlobals::Get()->GetFileTaskRunner(),
[email protected]914a4ba92013-08-10 13:38:48355 FROM_HERE,
356 Bind(&FileIOResource::ReadOp::DoWork, read_op),
357 RunWhileLocked(Bind(&TrackedCallback::Run, callback)));
358 callback->set_completion_task(
359 Bind(&FileIOResource::OnReadComplete, this, read_op, array_output));
360
[email protected]d45f29b2013-08-09 04:18:06361 return PP_OK_COMPLETIONPENDING;
362}
363
[email protected]914a4ba92013-08-10 13:38:48364int32_t FileIOResource::OnQueryComplete(scoped_refptr<QueryOp> query_op,
365 PP_FileInfo* info,
366 int32_t result) {
367 DCHECK(state_manager_.get_pending_operation() ==
368 FileIOStateManager::OPERATION_EXCLUSIVE);
369
370 if (result == PP_OK) {
371 // This writes the file info into the plugin's PP_FileInfo struct.
372 ppapi::PlatformFileInfoToPepperFileInfo(query_op->file_info(),
373 file_system_type_,
374 info);
375 }
376 state_manager_.SetOperationFinished();
377 return result;
378}
379
380int32_t FileIOResource::OnReadComplete(scoped_refptr<ReadOp> read_op,
381 PP_ArrayOutput array_output,
382 int32_t result) {
383 DCHECK(state_manager_.get_pending_operation() ==
384 FileIOStateManager::OPERATION_READ);
385 if (result >= 0) {
386 ArrayWriter output;
387 output.set_pp_array_output(array_output);
388 if (output.is_valid())
389 output.StoreArray(read_op->buffer(), result);
390 else
391 result = PP_ERROR_FAILED;
392 } else {
393 // The read operation failed.
394 result = PP_ERROR_FAILED;
395 }
396 state_manager_.SetOperationFinished();
397 return result;
398}
399
[email protected]c77144722013-01-19 04:16:36400void FileIOResource::OnPluginMsgGeneralComplete(
401 scoped_refptr<TrackedCallback> callback,
402 const ResourceMessageReplyParams& params) {
403 DCHECK(state_manager_.get_pending_operation() ==
404 FileIOStateManager::OPERATION_EXCLUSIVE ||
405 state_manager_.get_pending_operation() ==
406 FileIOStateManager::OPERATION_WRITE);
[email protected]d45f29b2013-08-09 04:18:06407 // End this operation now, so the user's callback can execute another FileIO
408 // operation, assuming there are no other pending operations.
[email protected]c77144722013-01-19 04:16:36409 state_manager_.SetOperationFinished();
410 callback->Run(params.result());
411}
412
413void FileIOResource::OnPluginMsgOpenFileComplete(
414 scoped_refptr<TrackedCallback> callback,
415 const ResourceMessageReplyParams& params) {
416 DCHECK(state_manager_.get_pending_operation() ==
417 FileIOStateManager::OPERATION_EXCLUSIVE);
[email protected]c6420f082013-09-18 22:42:41418
419 // Release the FileRef resource.
420 file_ref_ = NULL;
[email protected]c77144722013-01-19 04:16:36421 if (params.result() == PP_OK)
422 state_manager_.SetOpenSucceed();
[email protected]d45f29b2013-08-09 04:18:06423
424 int32_t result = params.result();
425 IPC::PlatformFileForTransit transit_file;
[email protected]d35d3f42013-10-02 19:52:20426 if ((result == PP_OK) && params.TakeFileHandleAtIndex(0, &transit_file)) {
427 file_handle_ = new FileHandleHolder(
428 IPC::PlatformFileForTransitToPlatformFile(transit_file));
429 }
[email protected]d45f29b2013-08-09 04:18:06430 // End this operation now, so the user's callback can execute another FileIO
431 // operation, assuming there are no other pending operations.
[email protected]23e78de2013-07-26 20:28:47432 state_manager_.SetOperationFinished();
[email protected]d45f29b2013-08-09 04:18:06433 callback->Run(result);
[email protected]23e78de2013-07-26 20:28:47434}
435
[email protected]8f96cef2013-04-01 16:51:13436void FileIOResource::OnPluginMsgRequestOSFileHandleComplete(
437 scoped_refptr<TrackedCallback> callback,
438 PP_FileHandle* output_handle,
439 const ResourceMessageReplyParams& params) {
440 DCHECK(state_manager_.get_pending_operation() ==
441 FileIOStateManager::OPERATION_EXCLUSIVE);
442
443 if (!TrackedCallback::IsPending(callback)) {
444 state_manager_.SetOperationFinished();
445 return;
446 }
447
448 int32_t result = params.result();
449 IPC::PlatformFileForTransit transit_file;
450 if (!params.TakeFileHandleAtIndex(0, &transit_file))
451 result = PP_ERROR_FAILED;
452 *output_handle = IPC::PlatformFileForTransitToPlatformFile(transit_file);
453
[email protected]d45f29b2013-08-09 04:18:06454 // End this operation now, so the user's callback can execute another FileIO
455 // operation, assuming there are no other pending operations.
[email protected]8f96cef2013-04-01 16:51:13456 state_manager_.SetOperationFinished();
457 callback->Run(result);
458}
459
[email protected]c77144722013-01-19 04:16:36460} // namespace proxy
461} // namespace ppapi