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.
5 #include "ppapi/proxy/file_io_resource.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_type_conversion.h"
14 #include "ppapi/shared_impl/ppapi_globals.h"
15 #include "ppapi/shared_impl/proxy_lock.h"
16 #include "ppapi/shared_impl/resource_tracker.h"
17 #include "ppapi/thunk/enter.h"
18 #include "ppapi/thunk/ppb_file_ref_api.h"
20 using ppapi::thunk::EnterResourceNoLock
;
21 using ppapi::thunk::PPB_FileIO_API
;
22 using ppapi::thunk::PPB_FileRef_API
;
26 // 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.
29 static const int32_t kMaxReadSize
= 32 * 1024 * 1024; // 32MB
31 // An adapter to let Read() share the same implementation with ReadToArray().
32 void* DummyGetDataBuffer(void* user_data
, uint32_t count
, uint32_t size
) {
36 // File thread task to close the file handle.
37 void DoClose(base::PlatformFile file
) {
38 base::ClosePlatformFile(file
);
46 FileIOResource::QueryOp::QueryOp(PP_FileHandle file_handle
)
47 : file_handle_(file_handle
) {
50 FileIOResource::QueryOp::~QueryOp() {
53 int32_t FileIOResource::QueryOp::DoWork() {
54 return base::GetPlatformFileInfo(file_handle_
, &file_info_
) ?
55 PP_OK
: PP_ERROR_FAILED
;
58 FileIOResource::ReadOp::ReadOp(PP_FileHandle file_handle
,
60 int32_t bytes_to_read
)
61 : file_handle_(file_handle
),
63 bytes_to_read_(bytes_to_read
) {
66 FileIOResource::ReadOp::~ReadOp() {
69 int32_t FileIOResource::ReadOp::DoWork() {
70 DCHECK(!buffer_
.get());
71 buffer_
.reset(new char[bytes_to_read_
]);
72 return base::ReadPlatformFile(
73 file_handle_
, offset_
, buffer_
.get(), bytes_to_read_
);
76 FileIOResource::FileIOResource(Connection connection
, PP_Instance instance
)
77 : PluginResource(connection
, instance
),
78 file_handle_(base::kInvalidPlatformFileValue
),
79 file_system_type_(PP_FILESYSTEMTYPE_INVALID
) {
80 SendCreate(RENDERER
, PpapiHostMsg_FileIO_Create());
83 FileIOResource::~FileIOResource() {
87 PPB_FileIO_API
* FileIOResource::AsPPB_FileIO_API() {
91 int32_t FileIOResource::Open(PP_Resource file_ref
,
93 scoped_refptr
<TrackedCallback
> callback
) {
94 EnterResourceNoLock
<PPB_FileRef_API
> enter(file_ref
, true);
96 return PP_ERROR_BADRESOURCE
;
98 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
) {
105 return PP_ERROR_FAILED
;
107 file_system_type_
= type
;
109 int32_t rv
= state_manager_
.CheckOperationState(
110 FileIOStateManager::OPERATION_EXCLUSIVE
, false);
114 Call
<PpapiPluginMsg_FileIO_OpenReply
>(RENDERER
,
115 PpapiHostMsg_FileIO_Open(
116 enter
.resource()->host_resource().host_resource(),
118 base::Bind(&FileIOResource::OnPluginMsgOpenFileComplete
, this,
121 state_manager_
.SetPendingOperation(FileIOStateManager::OPERATION_EXCLUSIVE
);
122 return PP_OK_COMPLETIONPENDING
;
125 int32_t FileIOResource::Query(PP_FileInfo
* info
,
126 scoped_refptr
<TrackedCallback
> callback
) {
127 int32_t rv
= state_manager_
.CheckOperationState(
128 FileIOStateManager::OPERATION_EXCLUSIVE
, true);
132 return PP_ERROR_BADARGUMENT
;
133 if (file_handle_
== base::kInvalidPlatformFileValue
)
134 return PP_ERROR_FAILED
;
136 state_manager_
.SetPendingOperation(FileIOStateManager::OPERATION_EXCLUSIVE
);
137 scoped_refptr
<QueryOp
> query_op(new QueryOp(file_handle_
));
139 // If the callback is blocking, perform the task on the calling thread.
140 if (callback
->is_blocking()) {
143 // Release the proxy lock while making a potentially slow file call.
144 ProxyAutoUnlock unlock
;
145 result
= query_op
->DoWork();
147 return OnQueryComplete(query_op
, info
, result
);
150 // For the non-blocking case, post a task to the file thread and add a
151 // completion task to write the result.
152 base::PostTaskAndReplyWithResult(
153 PpapiGlobals::Get()->GetFileTaskRunner(pp_instance()),
155 Bind(&FileIOResource::QueryOp::DoWork
, query_op
),
156 RunWhileLocked(Bind(&TrackedCallback::Run
, callback
)));
157 callback
->set_completion_task(
158 Bind(&FileIOResource::OnQueryComplete
, this, query_op
, info
));
160 return PP_OK_COMPLETIONPENDING
;
163 int32_t FileIOResource::Touch(PP_Time last_access_time
,
164 PP_Time last_modified_time
,
165 scoped_refptr
<TrackedCallback
> callback
) {
166 int32_t rv
= state_manager_
.CheckOperationState(
167 FileIOStateManager::OPERATION_EXCLUSIVE
, true);
171 Call
<PpapiPluginMsg_FileIO_GeneralReply
>(RENDERER
,
172 PpapiHostMsg_FileIO_Touch(last_access_time
, last_modified_time
),
173 base::Bind(&FileIOResource::OnPluginMsgGeneralComplete
, this,
176 state_manager_
.SetPendingOperation(FileIOStateManager::OPERATION_EXCLUSIVE
);
177 return PP_OK_COMPLETIONPENDING
;
180 int32_t FileIOResource::Read(int64_t offset
,
182 int32_t bytes_to_read
,
183 scoped_refptr
<TrackedCallback
> callback
) {
184 int32_t rv
= state_manager_
.CheckOperationState(
185 FileIOStateManager::OPERATION_READ
, true);
189 PP_ArrayOutput output_adapter
;
190 output_adapter
.GetDataBuffer
= &DummyGetDataBuffer
;
191 output_adapter
.user_data
= buffer
;
192 return ReadValidated(offset
, bytes_to_read
, output_adapter
, callback
);
195 int32_t FileIOResource::ReadToArray(int64_t offset
,
196 int32_t max_read_length
,
197 PP_ArrayOutput
* array_output
,
198 scoped_refptr
<TrackedCallback
> callback
) {
199 DCHECK(array_output
);
200 int32_t rv
= state_manager_
.CheckOperationState(
201 FileIOStateManager::OPERATION_READ
, true);
205 return ReadValidated(offset
, max_read_length
, *array_output
, callback
);
208 int32_t FileIOResource::Write(int64_t offset
,
210 int32_t bytes_to_write
,
211 scoped_refptr
<TrackedCallback
> callback
) {
212 int32_t rv
= state_manager_
.CheckOperationState(
213 FileIOStateManager::OPERATION_WRITE
, true);
217 // TODO(brettw) it would be nice to use a shared memory buffer for large
218 // writes rather than having to copy to a string (which will involve a number
219 // of extra copies to serialize over IPC).
220 Call
<PpapiPluginMsg_FileIO_GeneralReply
>(RENDERER
,
221 PpapiHostMsg_FileIO_Write(offset
, std::string(buffer
, bytes_to_write
)),
222 base::Bind(&FileIOResource::OnPluginMsgGeneralComplete
, this,
225 state_manager_
.SetPendingOperation(FileIOStateManager::OPERATION_WRITE
);
226 return PP_OK_COMPLETIONPENDING
;
229 int32_t FileIOResource::SetLength(int64_t length
,
230 scoped_refptr
<TrackedCallback
> callback
) {
231 int32_t rv
= state_manager_
.CheckOperationState(
232 FileIOStateManager::OPERATION_EXCLUSIVE
, true);
236 Call
<PpapiPluginMsg_FileIO_GeneralReply
>(RENDERER
,
237 PpapiHostMsg_FileIO_SetLength(length
),
238 base::Bind(&FileIOResource::OnPluginMsgGeneralComplete
, this,
241 state_manager_
.SetPendingOperation(FileIOStateManager::OPERATION_EXCLUSIVE
);
242 return PP_OK_COMPLETIONPENDING
;
245 int32_t FileIOResource::Flush(scoped_refptr
<TrackedCallback
> callback
) {
246 int32_t rv
= state_manager_
.CheckOperationState(
247 FileIOStateManager::OPERATION_EXCLUSIVE
, true);
251 Call
<PpapiPluginMsg_FileIO_GeneralReply
>(RENDERER
,
252 PpapiHostMsg_FileIO_Flush(),
253 base::Bind(&FileIOResource::OnPluginMsgGeneralComplete
, this,
256 state_manager_
.SetPendingOperation(FileIOStateManager::OPERATION_EXCLUSIVE
);
257 return PP_OK_COMPLETIONPENDING
;
260 void FileIOResource::Close() {
262 Post(RENDERER
, PpapiHostMsg_FileIO_Close());
265 int32_t FileIOResource::GetOSFileDescriptor() {
266 int32_t file_descriptor
;
267 // Only available when running in process.
268 SyncCall
<PpapiPluginMsg_FileIO_GetOSFileDescriptorReply
>(
269 RENDERER
, PpapiHostMsg_FileIO_GetOSFileDescriptor(), &file_descriptor
);
270 return file_descriptor
;
273 int32_t FileIOResource::RequestOSFileHandle(
274 PP_FileHandle
* handle
,
275 scoped_refptr
<TrackedCallback
> callback
) {
276 int32_t rv
= state_manager_
.CheckOperationState(
277 FileIOStateManager::OPERATION_EXCLUSIVE
, true);
281 Call
<PpapiPluginMsg_FileIO_RequestOSFileHandleReply
>(RENDERER
,
282 PpapiHostMsg_FileIO_RequestOSFileHandle(),
283 base::Bind(&FileIOResource::OnPluginMsgRequestOSFileHandleComplete
, this,
286 state_manager_
.SetPendingOperation(FileIOStateManager::OPERATION_EXCLUSIVE
);
287 return PP_OK_COMPLETIONPENDING
;
290 int32_t FileIOResource::WillWrite(int64_t offset
,
291 int32_t bytes_to_write
,
292 scoped_refptr
<TrackedCallback
> callback
) {
293 Call
<PpapiPluginMsg_FileIO_GeneralReply
>(RENDERER
,
294 PpapiHostMsg_FileIO_WillWrite(offset
, bytes_to_write
),
295 base::Bind(&FileIOResource::OnPluginMsgGeneralComplete
, this, callback
));
297 state_manager_
.SetPendingOperation(FileIOStateManager::OPERATION_EXCLUSIVE
);
298 return PP_OK_COMPLETIONPENDING
;
301 int32_t FileIOResource::WillSetLength(int64_t length
,
302 scoped_refptr
<TrackedCallback
> callback
) {
303 Call
<PpapiPluginMsg_FileIO_GeneralReply
>(RENDERER
,
304 PpapiHostMsg_FileIO_WillSetLength(length
),
305 base::Bind(&FileIOResource::OnPluginMsgGeneralComplete
, this, callback
));
307 state_manager_
.SetPendingOperation(FileIOStateManager::OPERATION_EXCLUSIVE
);
308 return PP_OK_COMPLETIONPENDING
;
311 int32_t FileIOResource::ReadValidated(int64_t offset
,
312 int32_t bytes_to_read
,
313 const PP_ArrayOutput
& array_output
,
314 scoped_refptr
<TrackedCallback
> callback
) {
315 if (bytes_to_read
< 0)
316 return PP_ERROR_FAILED
;
317 if (file_handle_
== base::kInvalidPlatformFileValue
)
318 return PP_ERROR_FAILED
;
320 state_manager_
.SetPendingOperation(FileIOStateManager::OPERATION_READ
);
322 bytes_to_read
= std::min(bytes_to_read
, kMaxReadSize
);
323 scoped_refptr
<ReadOp
> read_op(
324 new ReadOp(file_handle_
, offset
, bytes_to_read
));
325 if (callback
->is_blocking()) {
328 // Release the proxy lock while making a potentially slow file call.
329 ProxyAutoUnlock unlock
;
330 result
= read_op
->DoWork();
332 return OnReadComplete(read_op
, array_output
, result
);
335 // For the non-blocking case, post a task to the file thread.
336 base::PostTaskAndReplyWithResult(
337 PpapiGlobals::Get()->GetFileTaskRunner(pp_instance()),
339 Bind(&FileIOResource::ReadOp::DoWork
, read_op
),
340 RunWhileLocked(Bind(&TrackedCallback::Run
, callback
)));
341 callback
->set_completion_task(
342 Bind(&FileIOResource::OnReadComplete
, this, read_op
, array_output
));
344 return PP_OK_COMPLETIONPENDING
;
347 void FileIOResource::CloseFileHandle() {
348 if (file_handle_
!= base::kInvalidPlatformFileValue
) {
349 // Close our local fd on the file thread.
350 base::TaskRunner
* file_task_runner
=
351 PpapiGlobals::Get()->GetFileTaskRunner(pp_instance());
352 file_task_runner
->PostTask(FROM_HERE
,
353 base::Bind(&DoClose
, file_handle_
));
355 file_handle_
= base::kInvalidPlatformFileValue
;
359 int32_t FileIOResource::OnQueryComplete(scoped_refptr
<QueryOp
> query_op
,
362 DCHECK(state_manager_
.get_pending_operation() ==
363 FileIOStateManager::OPERATION_EXCLUSIVE
);
365 if (result
== PP_OK
) {
366 // This writes the file info into the plugin's PP_FileInfo struct.
367 ppapi::PlatformFileInfoToPepperFileInfo(query_op
->file_info(),
371 state_manager_
.SetOperationFinished();
375 int32_t FileIOResource::OnReadComplete(scoped_refptr
<ReadOp
> read_op
,
376 PP_ArrayOutput array_output
,
378 DCHECK(state_manager_
.get_pending_operation() ==
379 FileIOStateManager::OPERATION_READ
);
382 output
.set_pp_array_output(array_output
);
383 if (output
.is_valid())
384 output
.StoreArray(read_op
->buffer(), result
);
386 result
= PP_ERROR_FAILED
;
388 // The read operation failed.
389 result
= PP_ERROR_FAILED
;
391 state_manager_
.SetOperationFinished();
395 void FileIOResource::OnPluginMsgGeneralComplete(
396 scoped_refptr
<TrackedCallback
> callback
,
397 const ResourceMessageReplyParams
& params
) {
398 DCHECK(state_manager_
.get_pending_operation() ==
399 FileIOStateManager::OPERATION_EXCLUSIVE
||
400 state_manager_
.get_pending_operation() ==
401 FileIOStateManager::OPERATION_WRITE
);
402 // End this operation now, so the user's callback can execute another FileIO
403 // operation, assuming there are no other pending operations.
404 state_manager_
.SetOperationFinished();
405 callback
->Run(params
.result());
408 void FileIOResource::OnPluginMsgOpenFileComplete(
409 scoped_refptr
<TrackedCallback
> callback
,
410 const ResourceMessageReplyParams
& params
) {
411 DCHECK(state_manager_
.get_pending_operation() ==
412 FileIOStateManager::OPERATION_EXCLUSIVE
);
413 if (params
.result() == PP_OK
)
414 state_manager_
.SetOpenSucceed();
416 int32_t result
= params
.result();
417 IPC::PlatformFileForTransit transit_file
;
418 if ((result
== PP_OK
) && params
.TakeFileHandleAtIndex(0, &transit_file
))
419 file_handle_
= IPC::PlatformFileForTransitToPlatformFile(transit_file
);
420 // End this operation now, so the user's callback can execute another FileIO
421 // operation, assuming there are no other pending operations.
422 state_manager_
.SetOperationFinished();
423 callback
->Run(result
);
426 void FileIOResource::OnPluginMsgRequestOSFileHandleComplete(
427 scoped_refptr
<TrackedCallback
> callback
,
428 PP_FileHandle
* output_handle
,
429 const ResourceMessageReplyParams
& params
) {
430 DCHECK(state_manager_
.get_pending_operation() ==
431 FileIOStateManager::OPERATION_EXCLUSIVE
);
433 if (!TrackedCallback::IsPending(callback
)) {
434 state_manager_
.SetOperationFinished();
438 int32_t result
= params
.result();
439 IPC::PlatformFileForTransit transit_file
;
440 if (!params
.TakeFileHandleAtIndex(0, &transit_file
))
441 result
= PP_ERROR_FAILED
;
442 *output_handle
= IPC::PlatformFileForTransitToPlatformFile(transit_file
);
444 // End this operation now, so the user's callback can execute another FileIO
445 // operation, assuming there are no other pending operations.
446 state_manager_
.SetOperationFinished();
447 callback
->Run(result
);