blob: b46b31a953ebf1707874d920bff088946db7049e [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]26dfb4b72013-11-12 19:36:1313#include "ppapi/shared_impl/file_system_util.h"
[email protected]d45f29b2013-08-09 04:18:0614#include "ppapi/shared_impl/file_type_conversion.h"
[email protected]c77144722013-01-19 04:16:3615#include "ppapi/shared_impl/ppapi_globals.h"
[email protected]d45f29b2013-08-09 04:18:0616#include "ppapi/shared_impl/proxy_lock.h"
[email protected]c77144722013-01-19 04:16:3617#include "ppapi/shared_impl/resource_tracker.h"
18#include "ppapi/thunk/enter.h"
19#include "ppapi/thunk/ppb_file_ref_api.h"
20
21using ppapi::thunk::EnterResourceNoLock;
22using ppapi::thunk::PPB_FileIO_API;
23using ppapi::thunk::PPB_FileRef_API;
24
25namespace {
26
[email protected]914a4ba92013-08-10 13:38:4827// We must allocate a buffer sized according to the request of the plugin. To
28// reduce the chance of out-of-memory errors, we cap the read size to 32MB.
29// This is OK since the API specifies that it may perform a partial read.
30static const int32_t kMaxReadSize = 32 * 1024 * 1024; // 32MB
31
[email protected]c77144722013-01-19 04:16:3632// An adapter to let Read() share the same implementation with ReadToArray().
33void* DummyGetDataBuffer(void* user_data, uint32_t count, uint32_t size) {
34 return user_data;
35}
36
[email protected]d45f29b2013-08-09 04:18:0637// File thread task to close the file handle.
38void DoClose(base::PlatformFile file) {
39 base::ClosePlatformFile(file);
40}
41
[email protected]c77144722013-01-19 04:16:3642} // namespace
43
44namespace ppapi {
45namespace proxy {
46
[email protected]d35d3f42013-10-02 19:52:2047FileIOResource::QueryOp::QueryOp(scoped_refptr<FileHandleHolder> file_handle)
[email protected]914a4ba92013-08-10 13:38:4848 : file_handle_(file_handle) {
[email protected]d35d3f42013-10-02 19:52:2049 DCHECK(file_handle_);
[email protected]914a4ba92013-08-10 13:38:4850}
51
52FileIOResource::QueryOp::~QueryOp() {
53}
54
55int32_t FileIOResource::QueryOp::DoWork() {
[email protected]d35d3f42013-10-02 19:52:2056 return base::GetPlatformFileInfo(file_handle_->raw_handle(), &file_info_) ?
[email protected]914a4ba92013-08-10 13:38:4857 PP_OK : PP_ERROR_FAILED;
58}
59
[email protected]d35d3f42013-10-02 19:52:2060FileIOResource::ReadOp::ReadOp(scoped_refptr<FileHandleHolder> file_handle,
[email protected]914a4ba92013-08-10 13:38:4861 int64_t offset,
62 int32_t bytes_to_read)
63 : file_handle_(file_handle),
64 offset_(offset),
65 bytes_to_read_(bytes_to_read) {
[email protected]d35d3f42013-10-02 19:52:2066 DCHECK(file_handle_);
[email protected]914a4ba92013-08-10 13:38:4867}
68
69FileIOResource::ReadOp::~ReadOp() {
70}
71
72int32_t FileIOResource::ReadOp::DoWork() {
73 DCHECK(!buffer_.get());
74 buffer_.reset(new char[bytes_to_read_]);
75 return base::ReadPlatformFile(
[email protected]d35d3f42013-10-02 19:52:2076 file_handle_->raw_handle(), offset_, buffer_.get(), bytes_to_read_);
[email protected]914a4ba92013-08-10 13:38:4877}
78
[email protected]c77144722013-01-19 04:16:3679FileIOResource::FileIOResource(Connection connection, PP_Instance instance)
[email protected]d45f29b2013-08-09 04:18:0680 : PluginResource(connection, instance),
[email protected]d45f29b2013-08-09 04:18:0681 file_system_type_(PP_FILESYSTEMTYPE_INVALID) {
[email protected]10c39222013-11-13 20:09:2582 SendCreate(BROWSER, PpapiHostMsg_FileIO_Create());
[email protected]c77144722013-01-19 04:16:3683}
84
85FileIOResource::~FileIOResource() {
86}
87
88PPB_FileIO_API* FileIOResource::AsPPB_FileIO_API() {
89 return this;
90}
91
92int32_t FileIOResource::Open(PP_Resource file_ref,
93 int32_t open_flags,
94 scoped_refptr<TrackedCallback> callback) {
95 EnterResourceNoLock<PPB_FileRef_API> enter(file_ref, true);
96 if (enter.failed())
97 return PP_ERROR_BADRESOURCE;
98
[email protected]d45f29b2013-08-09 04:18:0699 PPB_FileRef_API* file_ref_api = enter.object();
100 PP_FileSystemType type = file_ref_api->GetFileSystemType();
[email protected]26dfb4b72013-11-12 19:36:13101 if (!FileSystemTypeIsValid(type)) {
[email protected]d45f29b2013-08-09 04:18:06102 NOTREACHED();
103 return PP_ERROR_FAILED;
104 }
105 file_system_type_ = type;
106
[email protected]c77144722013-01-19 04:16:36107 int32_t rv = state_manager_.CheckOperationState(
108 FileIOStateManager::OPERATION_EXCLUSIVE, false);
109 if (rv != PP_OK)
110 return rv;
111
[email protected]c6420f082013-09-18 22:42:41112 // Take a reference on the FileRef resource while we're opening the file; we
113 // don't want the plugin destroying it during the Open operation.
114 file_ref_ = enter.resource();
115
[email protected]10c39222013-11-13 20:09:25116 Call<PpapiPluginMsg_FileIO_OpenReply>(BROWSER,
[email protected]c77144722013-01-19 04:16:36117 PpapiHostMsg_FileIO_Open(
[email protected]c6420f082013-09-18 22:42:41118 file_ref,
[email protected]c77144722013-01-19 04:16:36119 open_flags),
120 base::Bind(&FileIOResource::OnPluginMsgOpenFileComplete, this,
121 callback));
122
123 state_manager_.SetPendingOperation(FileIOStateManager::OPERATION_EXCLUSIVE);
124 return PP_OK_COMPLETIONPENDING;
125}
126
127int32_t FileIOResource::Query(PP_FileInfo* info,
128 scoped_refptr<TrackedCallback> callback) {
129 int32_t rv = state_manager_.CheckOperationState(
130 FileIOStateManager::OPERATION_EXCLUSIVE, true);
131 if (rv != PP_OK)
132 return rv;
[email protected]914a4ba92013-08-10 13:38:48133 if (!info)
134 return PP_ERROR_BADARGUMENT;
[email protected]d35d3f42013-10-02 19:52:20135 if (!FileHandleHolder::IsValid(file_handle_))
[email protected]914a4ba92013-08-10 13:38:48136 return PP_ERROR_FAILED;
[email protected]c77144722013-01-19 04:16:36137
138 state_manager_.SetPendingOperation(FileIOStateManager::OPERATION_EXCLUSIVE);
[email protected]914a4ba92013-08-10 13:38:48139
140 // If the callback is blocking, perform the task on the calling thread.
141 if (callback->is_blocking()) {
[email protected]452fb142013-10-23 01:49:43142 int32_t result = PP_ERROR_FAILED;
143 base::PlatformFileInfo file_info;
144 // The plugin could release its reference to this instance when we release
145 // the proxy lock below.
146 scoped_refptr<FileIOResource> protect(this);
[email protected]914a4ba92013-08-10 13:38:48147 {
148 // Release the proxy lock while making a potentially slow file call.
149 ProxyAutoUnlock unlock;
[email protected]452fb142013-10-23 01:49:43150 if (base::GetPlatformFileInfo(file_handle_->raw_handle(), &file_info))
151 result = PP_OK;
[email protected]914a4ba92013-08-10 13:38:48152 }
[email protected]452fb142013-10-23 01:49:43153 if (result == PP_OK) {
154 // This writes the file info into the plugin's PP_FileInfo struct.
155 ppapi::PlatformFileInfoToPepperFileInfo(file_info,
156 file_system_type_,
157 info);
158 }
159 state_manager_.SetOperationFinished();
160 return result;
[email protected]914a4ba92013-08-10 13:38:48161 }
162
163 // For the non-blocking case, post a task to the file thread and add a
164 // completion task to write the result.
[email protected]452fb142013-10-23 01:49:43165 scoped_refptr<QueryOp> query_op(new QueryOp(file_handle_));
[email protected]914a4ba92013-08-10 13:38:48166 base::PostTaskAndReplyWithResult(
[email protected]3b5c68582013-10-01 06:20:44167 PpapiGlobals::Get()->GetFileTaskRunner(),
[email protected]914a4ba92013-08-10 13:38:48168 FROM_HERE,
169 Bind(&FileIOResource::QueryOp::DoWork, query_op),
170 RunWhileLocked(Bind(&TrackedCallback::Run, callback)));
171 callback->set_completion_task(
172 Bind(&FileIOResource::OnQueryComplete, this, query_op, info));
173
[email protected]c77144722013-01-19 04:16:36174 return PP_OK_COMPLETIONPENDING;
175}
176
177int32_t FileIOResource::Touch(PP_Time last_access_time,
178 PP_Time last_modified_time,
179 scoped_refptr<TrackedCallback> callback) {
180 int32_t rv = state_manager_.CheckOperationState(
181 FileIOStateManager::OPERATION_EXCLUSIVE, true);
182 if (rv != PP_OK)
183 return rv;
184
[email protected]10c39222013-11-13 20:09:25185 Call<PpapiPluginMsg_FileIO_GeneralReply>(BROWSER,
[email protected]c77144722013-01-19 04:16:36186 PpapiHostMsg_FileIO_Touch(last_access_time, last_modified_time),
187 base::Bind(&FileIOResource::OnPluginMsgGeneralComplete, this,
188 callback));
189
190 state_manager_.SetPendingOperation(FileIOStateManager::OPERATION_EXCLUSIVE);
191 return PP_OK_COMPLETIONPENDING;
192}
193
194int32_t FileIOResource::Read(int64_t offset,
195 char* buffer,
196 int32_t bytes_to_read,
197 scoped_refptr<TrackedCallback> callback) {
198 int32_t rv = state_manager_.CheckOperationState(
199 FileIOStateManager::OPERATION_READ, true);
200 if (rv != PP_OK)
201 return rv;
202
203 PP_ArrayOutput output_adapter;
204 output_adapter.GetDataBuffer = &DummyGetDataBuffer;
205 output_adapter.user_data = buffer;
[email protected]c77144722013-01-19 04:16:36206 return ReadValidated(offset, bytes_to_read, output_adapter, callback);
207}
208
209int32_t FileIOResource::ReadToArray(int64_t offset,
210 int32_t max_read_length,
211 PP_ArrayOutput* array_output,
212 scoped_refptr<TrackedCallback> callback) {
213 DCHECK(array_output);
214 int32_t rv = state_manager_.CheckOperationState(
215 FileIOStateManager::OPERATION_READ, true);
216 if (rv != PP_OK)
217 return rv;
218
[email protected]c77144722013-01-19 04:16:36219 return ReadValidated(offset, max_read_length, *array_output, callback);
220}
221
222int32_t FileIOResource::Write(int64_t offset,
223 const char* buffer,
224 int32_t bytes_to_write,
225 scoped_refptr<TrackedCallback> callback) {
226 int32_t rv = state_manager_.CheckOperationState(
227 FileIOStateManager::OPERATION_WRITE, true);
228 if (rv != PP_OK)
229 return rv;
230
231 // TODO(brettw) it would be nice to use a shared memory buffer for large
232 // writes rather than having to copy to a string (which will involve a number
233 // of extra copies to serialize over IPC).
[email protected]10c39222013-11-13 20:09:25234 Call<PpapiPluginMsg_FileIO_GeneralReply>(BROWSER,
[email protected]c77144722013-01-19 04:16:36235 PpapiHostMsg_FileIO_Write(offset, std::string(buffer, bytes_to_write)),
236 base::Bind(&FileIOResource::OnPluginMsgGeneralComplete, this,
237 callback));
238
239 state_manager_.SetPendingOperation(FileIOStateManager::OPERATION_WRITE);
240 return PP_OK_COMPLETIONPENDING;
241}
242
243int32_t FileIOResource::SetLength(int64_t length,
244 scoped_refptr<TrackedCallback> callback) {
245 int32_t rv = state_manager_.CheckOperationState(
246 FileIOStateManager::OPERATION_EXCLUSIVE, true);
247 if (rv != PP_OK)
248 return rv;
249
[email protected]10c39222013-11-13 20:09:25250 Call<PpapiPluginMsg_FileIO_GeneralReply>(BROWSER,
[email protected]c77144722013-01-19 04:16:36251 PpapiHostMsg_FileIO_SetLength(length),
252 base::Bind(&FileIOResource::OnPluginMsgGeneralComplete, this,
253 callback));
254
255 state_manager_.SetPendingOperation(FileIOStateManager::OPERATION_EXCLUSIVE);
256 return PP_OK_COMPLETIONPENDING;
257}
258
259int32_t FileIOResource::Flush(scoped_refptr<TrackedCallback> callback) {
260 int32_t rv = state_manager_.CheckOperationState(
261 FileIOStateManager::OPERATION_EXCLUSIVE, true);
262 if (rv != PP_OK)
263 return rv;
264
[email protected]10c39222013-11-13 20:09:25265 Call<PpapiPluginMsg_FileIO_GeneralReply>(BROWSER,
[email protected]c77144722013-01-19 04:16:36266 PpapiHostMsg_FileIO_Flush(),
267 base::Bind(&FileIOResource::OnPluginMsgGeneralComplete, this,
268 callback));
269
270 state_manager_.SetPendingOperation(FileIOStateManager::OPERATION_EXCLUSIVE);
271 return PP_OK_COMPLETIONPENDING;
272}
273
274void FileIOResource::Close() {
[email protected]d35d3f42013-10-02 19:52:20275 if (file_handle_) {
276 file_handle_ = NULL;
277 }
[email protected]10c39222013-11-13 20:09:25278 Post(BROWSER, PpapiHostMsg_FileIO_Close());
[email protected]c77144722013-01-19 04:16:36279}
280
[email protected]8f96cef2013-04-01 16:51:13281int32_t FileIOResource::RequestOSFileHandle(
282 PP_FileHandle* handle,
283 scoped_refptr<TrackedCallback> callback) {
284 int32_t rv = state_manager_.CheckOperationState(
285 FileIOStateManager::OPERATION_EXCLUSIVE, true);
286 if (rv != PP_OK)
287 return rv;
288
[email protected]10c39222013-11-13 20:09:25289 Call<PpapiPluginMsg_FileIO_RequestOSFileHandleReply>(BROWSER,
[email protected]8f96cef2013-04-01 16:51:13290 PpapiHostMsg_FileIO_RequestOSFileHandle(),
291 base::Bind(&FileIOResource::OnPluginMsgRequestOSFileHandleComplete, this,
292 callback, handle));
293
294 state_manager_.SetPendingOperation(FileIOStateManager::OPERATION_EXCLUSIVE);
295 return PP_OK_COMPLETIONPENDING;
296}
297
[email protected]d35d3f42013-10-02 19:52:20298FileIOResource::FileHandleHolder::FileHandleHolder(PP_FileHandle file_handle)
299 : raw_handle_(file_handle) {
300}
301
302// static
303bool FileIOResource::FileHandleHolder::IsValid(
304 const scoped_refptr<FileIOResource::FileHandleHolder>& handle) {
305 return handle && (handle->raw_handle() != base::kInvalidPlatformFileValue);
306}
307
308FileIOResource::FileHandleHolder::~FileHandleHolder() {
309 if (raw_handle_ != base::kInvalidPlatformFileValue) {
310 base::TaskRunner* file_task_runner =
311 PpapiGlobals::Get()->GetFileTaskRunner();
312 file_task_runner->PostTask(FROM_HERE,
313 base::Bind(&DoClose, raw_handle_));
314 }
315}
316
[email protected]d45f29b2013-08-09 04:18:06317int32_t FileIOResource::ReadValidated(int64_t offset,
318 int32_t bytes_to_read,
319 const PP_ArrayOutput& array_output,
320 scoped_refptr<TrackedCallback> callback) {
[email protected]914a4ba92013-08-10 13:38:48321 if (bytes_to_read < 0)
322 return PP_ERROR_FAILED;
[email protected]d35d3f42013-10-02 19:52:20323 if (!FileHandleHolder::IsValid(file_handle_))
[email protected]914a4ba92013-08-10 13:38:48324 return PP_ERROR_FAILED;
325
[email protected]d45f29b2013-08-09 04:18:06326 state_manager_.SetPendingOperation(FileIOStateManager::OPERATION_READ);
327
[email protected]914a4ba92013-08-10 13:38:48328 bytes_to_read = std::min(bytes_to_read, kMaxReadSize);
[email protected]914a4ba92013-08-10 13:38:48329 if (callback->is_blocking()) {
[email protected]452fb142013-10-23 01:49:43330 char* buffer = static_cast<char*>(
331 array_output.GetDataBuffer(array_output.user_data, bytes_to_read, 1));
332 int32_t result = PP_ERROR_FAILED;
333 // The plugin could release its reference to this instance when we release
334 // the proxy lock below.
335 scoped_refptr<FileIOResource> protect(this);
336 if (buffer) {
[email protected]914a4ba92013-08-10 13:38:48337 // Release the proxy lock while making a potentially slow file call.
338 ProxyAutoUnlock unlock;
[email protected]452fb142013-10-23 01:49:43339 result = base::ReadPlatformFile(
340 file_handle_->raw_handle(), offset, buffer, bytes_to_read);
341 if (result < 0)
342 result = PP_ERROR_FAILED;
[email protected]d45f29b2013-08-09 04:18:06343 }
[email protected]452fb142013-10-23 01:49:43344 state_manager_.SetOperationFinished();
345 return result;
[email protected]d45f29b2013-08-09 04:18:06346 }
347
[email protected]914a4ba92013-08-10 13:38:48348 // For the non-blocking case, post a task to the file thread.
[email protected]452fb142013-10-23 01:49:43349 scoped_refptr<ReadOp> read_op(
350 new ReadOp(file_handle_, offset, bytes_to_read));
[email protected]914a4ba92013-08-10 13:38:48351 base::PostTaskAndReplyWithResult(
[email protected]3b5c68582013-10-01 06:20:44352 PpapiGlobals::Get()->GetFileTaskRunner(),
[email protected]914a4ba92013-08-10 13:38:48353 FROM_HERE,
354 Bind(&FileIOResource::ReadOp::DoWork, read_op),
355 RunWhileLocked(Bind(&TrackedCallback::Run, callback)));
356 callback->set_completion_task(
357 Bind(&FileIOResource::OnReadComplete, this, read_op, array_output));
358
[email protected]d45f29b2013-08-09 04:18:06359 return PP_OK_COMPLETIONPENDING;
360}
361
[email protected]914a4ba92013-08-10 13:38:48362int32_t FileIOResource::OnQueryComplete(scoped_refptr<QueryOp> query_op,
363 PP_FileInfo* info,
364 int32_t result) {
365 DCHECK(state_manager_.get_pending_operation() ==
366 FileIOStateManager::OPERATION_EXCLUSIVE);
367
368 if (result == PP_OK) {
369 // This writes the file info into the plugin's PP_FileInfo struct.
370 ppapi::PlatformFileInfoToPepperFileInfo(query_op->file_info(),
371 file_system_type_,
372 info);
373 }
374 state_manager_.SetOperationFinished();
375 return result;
376}
377
378int32_t FileIOResource::OnReadComplete(scoped_refptr<ReadOp> read_op,
379 PP_ArrayOutput array_output,
380 int32_t result) {
381 DCHECK(state_manager_.get_pending_operation() ==
382 FileIOStateManager::OPERATION_READ);
383 if (result >= 0) {
384 ArrayWriter output;
385 output.set_pp_array_output(array_output);
386 if (output.is_valid())
387 output.StoreArray(read_op->buffer(), result);
388 else
389 result = PP_ERROR_FAILED;
390 } else {
391 // The read operation failed.
392 result = PP_ERROR_FAILED;
393 }
394 state_manager_.SetOperationFinished();
395 return result;
396}
397
[email protected]c77144722013-01-19 04:16:36398void FileIOResource::OnPluginMsgGeneralComplete(
399 scoped_refptr<TrackedCallback> callback,
400 const ResourceMessageReplyParams& params) {
401 DCHECK(state_manager_.get_pending_operation() ==
402 FileIOStateManager::OPERATION_EXCLUSIVE ||
403 state_manager_.get_pending_operation() ==
404 FileIOStateManager::OPERATION_WRITE);
[email protected]d45f29b2013-08-09 04:18:06405 // End this operation now, so the user's callback can execute another FileIO
406 // operation, assuming there are no other pending operations.
[email protected]c77144722013-01-19 04:16:36407 state_manager_.SetOperationFinished();
408 callback->Run(params.result());
409}
410
411void FileIOResource::OnPluginMsgOpenFileComplete(
412 scoped_refptr<TrackedCallback> callback,
413 const ResourceMessageReplyParams& params) {
414 DCHECK(state_manager_.get_pending_operation() ==
415 FileIOStateManager::OPERATION_EXCLUSIVE);
[email protected]c6420f082013-09-18 22:42:41416
417 // Release the FileRef resource.
418 file_ref_ = NULL;
[email protected]c77144722013-01-19 04:16:36419 if (params.result() == PP_OK)
420 state_manager_.SetOpenSucceed();
[email protected]d45f29b2013-08-09 04:18:06421
422 int32_t result = params.result();
423 IPC::PlatformFileForTransit transit_file;
[email protected]d35d3f42013-10-02 19:52:20424 if ((result == PP_OK) && params.TakeFileHandleAtIndex(0, &transit_file)) {
425 file_handle_ = new FileHandleHolder(
426 IPC::PlatformFileForTransitToPlatformFile(transit_file));
427 }
[email protected]d45f29b2013-08-09 04:18:06428 // End this operation now, so the user's callback can execute another FileIO
429 // operation, assuming there are no other pending operations.
[email protected]23e78de2013-07-26 20:28:47430 state_manager_.SetOperationFinished();
[email protected]d45f29b2013-08-09 04:18:06431 callback->Run(result);
[email protected]23e78de2013-07-26 20:28:47432}
433
[email protected]8f96cef2013-04-01 16:51:13434void FileIOResource::OnPluginMsgRequestOSFileHandleComplete(
435 scoped_refptr<TrackedCallback> callback,
436 PP_FileHandle* output_handle,
437 const ResourceMessageReplyParams& params) {
438 DCHECK(state_manager_.get_pending_operation() ==
439 FileIOStateManager::OPERATION_EXCLUSIVE);
440
441 if (!TrackedCallback::IsPending(callback)) {
442 state_manager_.SetOperationFinished();
443 return;
444 }
445
446 int32_t result = params.result();
447 IPC::PlatformFileForTransit transit_file;
448 if (!params.TakeFileHandleAtIndex(0, &transit_file))
449 result = PP_ERROR_FAILED;
450 *output_handle = IPC::PlatformFileForTransitToPlatformFile(transit_file);
451
[email protected]d45f29b2013-08-09 04:18:06452 // End this operation now, so the user's callback can execute another FileIO
453 // operation, assuming there are no other pending operations.
[email protected]8f96cef2013-04-01 16:51:13454 state_manager_.SetOperationFinished();
455 callback->Run(result);
456}
457
[email protected]c77144722013-01-19 04:16:36458} // namespace proxy
459} // namespace ppapi