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/files/file_util_proxy.h"
9 #include "ipc/ipc_message.h"
10 #include "ppapi/c/pp_errors.h"
11 #include "ppapi/proxy/plugin_globals.h"
12 #include "ppapi/proxy/ppapi_messages.h"
13 #include "ppapi/shared_impl/array_writer.h"
14 #include "ppapi/shared_impl/file_type_conversion.h"
15 #include "ppapi/shared_impl/ppapi_globals.h"
16 #include "ppapi/shared_impl/proxy_lock.h"
17 #include "ppapi/shared_impl/resource_tracker.h"
18 #include "ppapi/thunk/enter.h"
19 #include "ppapi/thunk/ppb_file_ref_api.h"
21 using ppapi::thunk::EnterResourceNoLock
;
22 using ppapi::thunk::PPB_FileIO_API
;
23 using ppapi::thunk::PPB_FileRef_API
;
27 // An adapter to let Read() share the same implementation with ReadToArray().
28 void* DummyGetDataBuffer(void* user_data
, uint32_t count
, uint32_t size
) {
32 // Dummy close callback allows us to call CloseFileHandle in the destructor.
33 void DummyCloseCallback(base::PlatformFileError error_code
) {
41 FileIOResource::FileIOResource(Connection connection
, PP_Instance instance
)
42 : PluginResource(connection
, instance
),
43 file_handle_(PP_kInvalidFileHandle
),
44 file_system_type_(PP_FILESYSTEMTYPE_INVALID
) {
45 SendCreate(RENDERER
, PpapiHostMsg_FileIO_Create());
48 FileIOResource::~FileIOResource() {
52 PPB_FileIO_API
* FileIOResource::AsPPB_FileIO_API() {
56 int32_t FileIOResource::Open(PP_Resource file_ref
,
58 scoped_refptr
<TrackedCallback
> callback
) {
59 EnterResourceNoLock
<PPB_FileRef_API
> enter(file_ref
, true);
61 return PP_ERROR_BADRESOURCE
;
63 PPB_FileRef_API
* file_ref_api
= enter
.object();
64 PP_FileSystemType type
= file_ref_api
->GetFileSystemType();
65 if (type
!= PP_FILESYSTEMTYPE_LOCALPERSISTENT
&&
66 type
!= PP_FILESYSTEMTYPE_LOCALTEMPORARY
&&
67 type
!= PP_FILESYSTEMTYPE_EXTERNAL
&&
68 type
!= PP_FILESYSTEMTYPE_ISOLATED
) {
70 return PP_ERROR_FAILED
;
72 file_system_type_
= type
;
74 int32_t rv
= state_manager_
.CheckOperationState(
75 FileIOStateManager::OPERATION_EXCLUSIVE
, false);
79 Call
<PpapiPluginMsg_FileIO_OpenReply
>(RENDERER
,
80 PpapiHostMsg_FileIO_Open(
81 enter
.resource()->host_resource().host_resource(),
83 base::Bind(&FileIOResource::OnPluginMsgOpenFileComplete
, this,
86 state_manager_
.SetPendingOperation(FileIOStateManager::OPERATION_EXCLUSIVE
);
87 return PP_OK_COMPLETIONPENDING
;
90 int32_t FileIOResource::Query(PP_FileInfo
* info
,
91 scoped_refptr
<TrackedCallback
> callback
) {
92 int32_t rv
= state_manager_
.CheckOperationState(
93 FileIOStateManager::OPERATION_EXCLUSIVE
, true);
97 if (file_handle_
== base::kInvalidPlatformFileValue
)
98 return PP_ERROR_FAILED
;
100 if (!base::FileUtilProxy::GetFileInfoFromPlatformFile(
101 PpapiGlobals::Get()->GetFileTaskRunner(pp_instance()),
103 RunWhileLocked(base::Bind(&FileIOResource::OnQueryComplete
, this,
105 return PP_ERROR_FAILED
;
108 state_manager_
.SetPendingOperation(FileIOStateManager::OPERATION_EXCLUSIVE
);
109 return PP_OK_COMPLETIONPENDING
;
112 int32_t FileIOResource::Touch(PP_Time last_access_time
,
113 PP_Time last_modified_time
,
114 scoped_refptr
<TrackedCallback
> callback
) {
115 int32_t rv
= state_manager_
.CheckOperationState(
116 FileIOStateManager::OPERATION_EXCLUSIVE
, true);
120 Call
<PpapiPluginMsg_FileIO_GeneralReply
>(RENDERER
,
121 PpapiHostMsg_FileIO_Touch(last_access_time
, last_modified_time
),
122 base::Bind(&FileIOResource::OnPluginMsgGeneralComplete
, this,
125 state_manager_
.SetPendingOperation(FileIOStateManager::OPERATION_EXCLUSIVE
);
126 return PP_OK_COMPLETIONPENDING
;
129 int32_t FileIOResource::Read(int64_t offset
,
131 int32_t bytes_to_read
,
132 scoped_refptr
<TrackedCallback
> callback
) {
133 int32_t rv
= state_manager_
.CheckOperationState(
134 FileIOStateManager::OPERATION_READ
, true);
138 PP_ArrayOutput output_adapter
;
139 output_adapter
.GetDataBuffer
= &DummyGetDataBuffer
;
140 output_adapter
.user_data
= buffer
;
141 return ReadValidated(offset
, bytes_to_read
, output_adapter
, callback
);
144 int32_t FileIOResource::ReadToArray(int64_t offset
,
145 int32_t max_read_length
,
146 PP_ArrayOutput
* array_output
,
147 scoped_refptr
<TrackedCallback
> callback
) {
148 DCHECK(array_output
);
149 int32_t rv
= state_manager_
.CheckOperationState(
150 FileIOStateManager::OPERATION_READ
, true);
154 return ReadValidated(offset
, max_read_length
, *array_output
, callback
);
157 int32_t FileIOResource::Write(int64_t offset
,
159 int32_t bytes_to_write
,
160 scoped_refptr
<TrackedCallback
> callback
) {
161 int32_t rv
= state_manager_
.CheckOperationState(
162 FileIOStateManager::OPERATION_WRITE
, true);
166 // TODO(brettw) it would be nice to use a shared memory buffer for large
167 // writes rather than having to copy to a string (which will involve a number
168 // of extra copies to serialize over IPC).
169 Call
<PpapiPluginMsg_FileIO_GeneralReply
>(RENDERER
,
170 PpapiHostMsg_FileIO_Write(offset
, std::string(buffer
, bytes_to_write
)),
171 base::Bind(&FileIOResource::OnPluginMsgGeneralComplete
, this,
174 state_manager_
.SetPendingOperation(FileIOStateManager::OPERATION_WRITE
);
175 return PP_OK_COMPLETIONPENDING
;
178 int32_t FileIOResource::SetLength(int64_t length
,
179 scoped_refptr
<TrackedCallback
> callback
) {
180 int32_t rv
= state_manager_
.CheckOperationState(
181 FileIOStateManager::OPERATION_EXCLUSIVE
, true);
185 Call
<PpapiPluginMsg_FileIO_GeneralReply
>(RENDERER
,
186 PpapiHostMsg_FileIO_SetLength(length
),
187 base::Bind(&FileIOResource::OnPluginMsgGeneralComplete
, this,
190 state_manager_
.SetPendingOperation(FileIOStateManager::OPERATION_EXCLUSIVE
);
191 return PP_OK_COMPLETIONPENDING
;
194 int32_t FileIOResource::Flush(scoped_refptr
<TrackedCallback
> callback
) {
195 int32_t rv
= state_manager_
.CheckOperationState(
196 FileIOStateManager::OPERATION_EXCLUSIVE
, true);
200 Call
<PpapiPluginMsg_FileIO_GeneralReply
>(RENDERER
,
201 PpapiHostMsg_FileIO_Flush(),
202 base::Bind(&FileIOResource::OnPluginMsgGeneralComplete
, this,
205 state_manager_
.SetPendingOperation(FileIOStateManager::OPERATION_EXCLUSIVE
);
206 return PP_OK_COMPLETIONPENDING
;
209 void FileIOResource::Close() {
211 Post(RENDERER
, PpapiHostMsg_FileIO_Close());
214 int32_t FileIOResource::GetOSFileDescriptor() {
215 int32_t file_descriptor
;
216 // Only available when running in process.
217 SyncCall
<PpapiPluginMsg_FileIO_GetOSFileDescriptorReply
>(
218 RENDERER
, PpapiHostMsg_FileIO_GetOSFileDescriptor(), &file_descriptor
);
219 return file_descriptor
;
222 int32_t FileIOResource::RequestOSFileHandle(
223 PP_FileHandle
* handle
,
224 scoped_refptr
<TrackedCallback
> callback
) {
225 int32_t rv
= state_manager_
.CheckOperationState(
226 FileIOStateManager::OPERATION_EXCLUSIVE
, true);
230 Call
<PpapiPluginMsg_FileIO_RequestOSFileHandleReply
>(RENDERER
,
231 PpapiHostMsg_FileIO_RequestOSFileHandle(),
232 base::Bind(&FileIOResource::OnPluginMsgRequestOSFileHandleComplete
, this,
235 state_manager_
.SetPendingOperation(FileIOStateManager::OPERATION_EXCLUSIVE
);
236 return PP_OK_COMPLETIONPENDING
;
239 int32_t FileIOResource::WillWrite(int64_t offset
,
240 int32_t bytes_to_write
,
241 scoped_refptr
<TrackedCallback
> callback
) {
242 Call
<PpapiPluginMsg_FileIO_GeneralReply
>(RENDERER
,
243 PpapiHostMsg_FileIO_WillWrite(offset
, bytes_to_write
),
244 base::Bind(&FileIOResource::OnPluginMsgGeneralComplete
, this,
247 state_manager_
.SetPendingOperation(FileIOStateManager::OPERATION_EXCLUSIVE
);
248 return PP_OK_COMPLETIONPENDING
;
251 int32_t FileIOResource::WillSetLength(int64_t length
,
252 scoped_refptr
<TrackedCallback
> callback
) {
253 Call
<PpapiPluginMsg_FileIO_GeneralReply
>(RENDERER
,
254 PpapiHostMsg_FileIO_WillSetLength(length
),
255 base::Bind(&FileIOResource::OnPluginMsgGeneralComplete
, this,
258 state_manager_
.SetPendingOperation(FileIOStateManager::OPERATION_EXCLUSIVE
);
259 return PP_OK_COMPLETIONPENDING
;
262 int32_t FileIOResource::ReadValidated(int64_t offset
,
263 int32_t bytes_to_read
,
264 const PP_ArrayOutput
& array_output
,
265 scoped_refptr
<TrackedCallback
> callback
) {
266 if (file_handle_
== base::kInvalidPlatformFileValue
)
267 return PP_ERROR_FAILED
;
269 if (!base::FileUtilProxy::Read(
270 PpapiGlobals::Get()->GetFileTaskRunner(pp_instance()),
274 RunWhileLocked(base::Bind(&FileIOResource::OnReadComplete
, this,
275 callback
, array_output
)))) {
276 return PP_ERROR_FAILED
;
279 state_manager_
.SetPendingOperation(FileIOStateManager::OPERATION_READ
);
280 return PP_OK_COMPLETIONPENDING
;
283 void FileIOResource::CloseFileHandle() {
284 if (file_handle_
!= base::kInvalidPlatformFileValue
) {
285 base::FileUtilProxy::Close(
286 PpapiGlobals::Get()->GetFileTaskRunner(pp_instance()),
288 base::Bind(&DummyCloseCallback
));
289 file_handle_
= base::kInvalidPlatformFileValue
;
293 void FileIOResource::OnQueryComplete(
294 scoped_refptr
<TrackedCallback
> callback
,
295 PP_FileInfo
* output_info
,
296 base::PlatformFileError error_code
,
297 const base::PlatformFileInfo
& file_info
) {
298 DCHECK(state_manager_
.get_pending_operation() ==
299 FileIOStateManager::OPERATION_EXCLUSIVE
);
301 if (!TrackedCallback::IsPending(callback
)) {
302 state_manager_
.SetOperationFinished();
306 int32_t result
= ::ppapi::PlatformFileErrorToPepperError(error_code
);
307 if (result
== PP_OK
) {
308 ppapi::PlatformFileInfoToPepperFileInfo(file_info
, file_system_type_
,
312 // End this operation now, so the user's callback can execute another FileIO
313 // operation, assuming there are no other pending operations.
314 state_manager_
.SetOperationFinished();
315 callback
->Run(result
);
318 void FileIOResource::OnReadComplete(
319 scoped_refptr
<TrackedCallback
> callback
,
320 PP_ArrayOutput array_output
,
321 base::PlatformFileError error_code
,
322 const char* data
, int bytes_read
) {
323 DCHECK(state_manager_
.get_pending_operation() ==
324 FileIOStateManager::OPERATION_READ
);
326 if (!TrackedCallback::IsPending(callback
)) {
327 state_manager_
.SetOperationFinished();
331 int32_t result
= ::ppapi::PlatformFileErrorToPepperError(error_code
);
332 if (result
== PP_OK
) {
333 result
= std::max(0, bytes_read
);
335 output
.set_pp_array_output(array_output
);
336 if (output
.is_valid())
337 output
.StoreArray(data
, result
);
339 result
= PP_ERROR_FAILED
;
342 // End this operation now, so the user's callback can execute another FileIO
343 // operation, assuming there are no other pending operations.
344 state_manager_
.SetOperationFinished();
345 callback
->Run(result
);
348 void FileIOResource::OnPluginMsgGeneralComplete(
349 scoped_refptr
<TrackedCallback
> callback
,
350 const ResourceMessageReplyParams
& params
) {
351 DCHECK(state_manager_
.get_pending_operation() ==
352 FileIOStateManager::OPERATION_EXCLUSIVE
||
353 state_manager_
.get_pending_operation() ==
354 FileIOStateManager::OPERATION_WRITE
);
355 // End this operation now, so the user's callback can execute another FileIO
356 // operation, assuming there are no other pending operations.
357 state_manager_
.SetOperationFinished();
358 callback
->Run(params
.result());
361 void FileIOResource::OnPluginMsgOpenFileComplete(
362 scoped_refptr
<TrackedCallback
> callback
,
363 const ResourceMessageReplyParams
& params
) {
364 DCHECK(state_manager_
.get_pending_operation() ==
365 FileIOStateManager::OPERATION_EXCLUSIVE
);
366 if (params
.result() == PP_OK
)
367 state_manager_
.SetOpenSucceed();
369 int32_t result
= params
.result();
370 IPC::PlatformFileForTransit transit_file
;
371 if (result
== PP_OK
&& !params
.TakeFileHandleAtIndex(0, &transit_file
))
372 result
= PP_ERROR_FAILED
;
373 file_handle_
= IPC::PlatformFileForTransitToPlatformFile(transit_file
);
375 // End this operation now, so the user's callback can execute another FileIO
376 // operation, assuming there are no other pending operations.
377 state_manager_
.SetOperationFinished();
378 callback
->Run(params
.result());
381 void FileIOResource::OnPluginMsgRequestOSFileHandleComplete(
382 scoped_refptr
<TrackedCallback
> callback
,
383 PP_FileHandle
* output_handle
,
384 const ResourceMessageReplyParams
& params
) {
385 DCHECK(state_manager_
.get_pending_operation() ==
386 FileIOStateManager::OPERATION_EXCLUSIVE
);
388 if (!TrackedCallback::IsPending(callback
)) {
389 state_manager_
.SetOperationFinished();
393 int32_t result
= params
.result();
394 IPC::PlatformFileForTransit transit_file
;
395 if (!params
.TakeFileHandleAtIndex(0, &transit_file
))
396 result
= PP_ERROR_FAILED
;
397 *output_handle
= IPC::PlatformFileForTransitToPlatformFile(transit_file
);
399 // End this operation now, so the user's callback can execute another FileIO
400 // operation, assuming there are no other pending operations.
401 state_manager_
.SetOperationFinished();
402 callback
->Run(result
);