1 // Copyright 2013 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 "content/browser/renderer_host/pepper/pepper_file_io_host.h"
8 #include "base/callback.h"
9 #include "base/callback_helpers.h"
10 #include "base/files/file_util_proxy.h"
11 #include "base/memory/weak_ptr.h"
12 #include "content/browser/renderer_host/pepper/pepper_file_ref_host.h"
13 #include "content/browser/renderer_host/pepper/pepper_file_system_browser_host.h"
14 #include "content/browser/renderer_host/pepper/pepper_security_helper.h"
15 #include "content/common/fileapi/file_system_messages.h"
16 #include "content/common/sandbox_util.h"
17 #include "content/common/view_messages.h"
18 #include "content/public/browser/browser_thread.h"
19 #include "content/public/browser/content_browser_client.h"
20 #include "content/public/browser/render_process_host.h"
21 #include "content/public/browser/storage_partition.h"
22 #include "content/public/common/content_client.h"
23 #include "ppapi/c/pp_errors.h"
24 #include "ppapi/c/ppb_file_io.h"
25 #include "ppapi/host/dispatch_host_message.h"
26 #include "ppapi/host/ppapi_host.h"
27 #include "ppapi/proxy/ppapi_messages.h"
28 #include "ppapi/shared_impl/file_system_util.h"
29 #include "ppapi/shared_impl/file_type_conversion.h"
30 #include "ppapi/shared_impl/time_conversion.h"
31 #include "webkit/browser/fileapi/file_observers.h"
32 #include "webkit/browser/fileapi/file_system_context.h"
33 #include "webkit/browser/fileapi/file_system_operation_runner.h"
34 #include "webkit/browser/fileapi/task_runner_bound_observer_list.h"
35 #include "webkit/common/fileapi/file_system_util.h"
39 using ppapi::FileIOStateManager
;
40 using ppapi::PPTimeToTime
;
44 PepperFileIOHost::UIThreadStuff
45 GetUIThreadStuffForInternalFileSystems(int render_process_id
) {
46 PepperFileIOHost::UIThreadStuff stuff
;
47 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
48 RenderProcessHost
* host
= RenderProcessHost::FromID(render_process_id
);
50 stuff
.resolved_render_process_id
= base::GetProcId(host
->GetHandle());
51 StoragePartition
* storage_partition
= host
->GetStoragePartition();
52 if (storage_partition
)
53 stuff
.file_system_context
= storage_partition
->GetFileSystemContext();
58 base::ProcessId
GetResolvedRenderProcessId(int render_process_id
) {
59 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
60 RenderProcessHost
* host
= RenderProcessHost::FromID(render_process_id
);
62 return base::kNullProcessId
;
63 return base::GetProcId(host
->GetHandle());
66 bool GetPluginAllowedToCallRequestOSFileHandle(int render_process_id
,
67 const GURL
& document_url
) {
68 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
69 ContentBrowserClient
* client
= GetContentClient()->browser();
70 RenderProcessHost
* host
= RenderProcessHost::FromID(render_process_id
);
71 return client
->IsPluginAllowedToCallRequestOSFileHandle(
72 host
->GetBrowserContext(), document_url
);
75 bool FileOpenForWrite(int32_t open_flags
) {
76 return (open_flags
& (PP_FILEOPENFLAG_WRITE
| PP_FILEOPENFLAG_APPEND
)) != 0;
81 PepperFileIOHost::PepperFileIOHost(BrowserPpapiHostImpl
* host
,
84 : ResourceHost(host
->GetPpapiHost(), instance
, resource
),
85 browser_ppapi_host_(host
),
86 render_process_host_(NULL
),
87 file_(base::kInvalidPlatformFileValue
),
89 file_system_type_(PP_FILESYSTEMTYPE_INVALID
),
90 max_written_offset_(0),
94 if (!host
->GetRenderFrameIDsForInstance(instance
,
97 render_process_id_
= -1;
99 file_message_loop_
= BrowserThread::GetMessageLoopProxyForThread(
100 BrowserThread::FILE);
103 PepperFileIOHost::~PepperFileIOHost() {
106 int32_t PepperFileIOHost::OnResourceMessageReceived(
107 const IPC::Message
& msg
,
108 ppapi::host::HostMessageContext
* context
) {
109 IPC_BEGIN_MESSAGE_MAP(PepperFileIOHost
, msg
)
110 PPAPI_DISPATCH_HOST_RESOURCE_CALL(PpapiHostMsg_FileIO_Open
,
112 PPAPI_DISPATCH_HOST_RESOURCE_CALL(PpapiHostMsg_FileIO_Touch
,
114 PPAPI_DISPATCH_HOST_RESOURCE_CALL(PpapiHostMsg_FileIO_SetLength
,
116 PPAPI_DISPATCH_HOST_RESOURCE_CALL_0(PpapiHostMsg_FileIO_Flush
,
118 PPAPI_DISPATCH_HOST_RESOURCE_CALL(PpapiHostMsg_FileIO_Close
,
120 PPAPI_DISPATCH_HOST_RESOURCE_CALL_0(PpapiHostMsg_FileIO_RequestOSFileHandle
,
121 OnHostMsgRequestOSFileHandle
)
122 IPC_END_MESSAGE_MAP()
123 return PP_ERROR_FAILED
;
126 PepperFileIOHost::UIThreadStuff::UIThreadStuff() {
127 resolved_render_process_id
= base::kNullProcessId
;
130 PepperFileIOHost::UIThreadStuff::~UIThreadStuff() {
133 int32_t PepperFileIOHost::OnHostMsgOpen(
134 ppapi::host::HostMessageContext
* context
,
135 PP_Resource file_ref_resource
,
136 int32_t open_flags
) {
137 int32_t rv
= state_manager_
.CheckOperationState(
138 FileIOStateManager::OPERATION_EXCLUSIVE
, false);
142 int platform_file_flags
= 0;
143 if (!ppapi::PepperFileOpenFlagsToPlatformFileFlags(open_flags
,
144 &platform_file_flags
))
145 return PP_ERROR_BADARGUMENT
;
147 ppapi::host::ResourceHost
* resource_host
=
148 host()->GetResourceHost(file_ref_resource
);
149 if (!resource_host
|| !resource_host
->IsFileRefHost())
150 return PP_ERROR_BADRESOURCE
;
151 PepperFileRefHost
* file_ref_host
=
152 static_cast<PepperFileRefHost
*>(resource_host
);
153 if (file_ref_host
->GetFileSystemType() == PP_FILESYSTEMTYPE_INVALID
)
154 return PP_ERROR_FAILED
;
156 file_system_host_
= file_ref_host
->GetFileSystemHost();
158 open_flags_
= open_flags
;
159 file_system_type_
= file_ref_host
->GetFileSystemType();
160 file_system_url_
= file_ref_host
->GetFileSystemURL();
162 if (file_system_type_
!= PP_FILESYSTEMTYPE_EXTERNAL
) {
163 if (!file_system_url_
.is_valid())
164 return PP_ERROR_BADARGUMENT
;
165 if (!CanOpenFileSystemURLWithPepperFlags(open_flags
,
168 return PP_ERROR_NOACCESS
;
169 BrowserThread::PostTaskAndReplyWithResult(
172 base::Bind(&GetUIThreadStuffForInternalFileSystems
,
174 base::Bind(&PepperFileIOHost::GotUIThreadStuffForInternalFileSystems
,
175 weak_factory_
.GetWeakPtr(),
176 context
->MakeReplyMessageContext(),
177 platform_file_flags
));
179 base::FilePath path
= file_ref_host
->GetExternalFilePath();
180 if (!CanOpenWithPepperFlags(open_flags
, render_process_id_
, path
))
181 return PP_ERROR_NOACCESS
;
182 BrowserThread::PostTaskAndReplyWithResult(
185 base::Bind(&GetResolvedRenderProcessId
, render_process_id_
),
186 base::Bind(&PepperFileIOHost::GotResolvedRenderProcessId
,
187 weak_factory_
.GetWeakPtr(),
188 context
->MakeReplyMessageContext(),
190 platform_file_flags
));
192 state_manager_
.SetPendingOperation(FileIOStateManager::OPERATION_EXCLUSIVE
);
193 return PP_OK_COMPLETIONPENDING
;
196 void PepperFileIOHost::GotUIThreadStuffForInternalFileSystems(
197 ppapi::host::ReplyMessageContext reply_context
,
198 int platform_file_flags
,
199 UIThreadStuff ui_thread_stuff
) {
200 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO
));
201 file_system_context_
= ui_thread_stuff
.file_system_context
;
202 resolved_render_process_id_
= ui_thread_stuff
.resolved_render_process_id
;
203 if (resolved_render_process_id_
== base::kNullProcessId
||
204 !file_system_context_
.get()) {
205 reply_context
.params
.set_result(PP_ERROR_FAILED
);
206 SendOpenErrorReply(reply_context
);
210 if (!file_system_context_
->GetFileSystemBackend(file_system_url_
.type())) {
211 reply_context
.params
.set_result(PP_ERROR_FAILED
);
212 SendOpenErrorReply(reply_context
);
216 DCHECK(file_system_host_
.get());
217 DCHECK(file_system_host_
->GetFileSystemOperationRunner());
218 file_system_host_
->GetFileSystemOperationRunner()->OpenFile(
221 base::Bind(&PepperFileIOHost::DidOpenInternalFile
,
222 weak_factory_
.GetWeakPtr(),
226 void PepperFileIOHost::DidOpenInternalFile(
227 ppapi::host::ReplyMessageContext reply_context
,
228 base::PlatformFileError result
,
229 base::PlatformFile file
,
230 const base::Closure
& on_close_callback
) {
231 if (result
== base::PLATFORM_FILE_OK
) {
232 on_close_callback_
= on_close_callback
;
234 if (FileOpenForWrite(open_flags_
) && file_system_host_
->ChecksQuota()) {
236 file_system_host_
->OpenQuotaFile(
238 file_system_url_
.path(),
239 base::Bind(&PepperFileIOHost::DidOpenQuotaFile
,
240 weak_factory_
.GetWeakPtr(),
247 ExecutePlatformOpenFileCallback(
248 reply_context
, result
, base::PassPlatformFile(&file
), true);
251 void PepperFileIOHost::GotResolvedRenderProcessId(
252 ppapi::host::ReplyMessageContext reply_context
,
254 int platform_file_flags
,
255 base::ProcessId resolved_render_process_id
) {
256 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO
));
257 resolved_render_process_id_
= resolved_render_process_id
;
258 base::FileUtilProxy::CreateOrOpen(
262 base::Bind(&PepperFileIOHost::ExecutePlatformOpenFileCallback
,
263 weak_factory_
.GetWeakPtr(),
267 int32_t PepperFileIOHost::OnHostMsgTouch(
268 ppapi::host::HostMessageContext
* context
,
269 PP_Time last_access_time
,
270 PP_Time last_modified_time
) {
271 int32_t rv
= state_manager_
.CheckOperationState(
272 FileIOStateManager::OPERATION_EXCLUSIVE
, true);
276 if (!base::FileUtilProxy::Touch(
279 PPTimeToTime(last_access_time
),
280 PPTimeToTime(last_modified_time
),
281 base::Bind(&PepperFileIOHost::ExecutePlatformGeneralCallback
,
282 weak_factory_
.GetWeakPtr(),
283 context
->MakeReplyMessageContext())))
284 return PP_ERROR_FAILED
;
286 state_manager_
.SetPendingOperation(FileIOStateManager::OPERATION_EXCLUSIVE
);
287 return PP_OK_COMPLETIONPENDING
;
290 int32_t PepperFileIOHost::OnHostMsgSetLength(
291 ppapi::host::HostMessageContext
* context
,
293 int32_t rv
= state_manager_
.CheckOperationState(
294 FileIOStateManager::OPERATION_EXCLUSIVE
, true);
298 return PP_ERROR_BADARGUMENT
;
300 // Quota checks are performed on the plugin side, in order to use the same
301 // quota reservation and request system as Write.
303 if (!base::FileUtilProxy::Truncate(
307 base::Bind(&PepperFileIOHost::ExecutePlatformGeneralCallback
,
308 weak_factory_
.GetWeakPtr(),
309 context
->MakeReplyMessageContext())))
310 return PP_ERROR_FAILED
;
312 state_manager_
.SetPendingOperation(FileIOStateManager::OPERATION_EXCLUSIVE
);
313 return PP_OK_COMPLETIONPENDING
;
316 int32_t PepperFileIOHost::OnHostMsgFlush(
317 ppapi::host::HostMessageContext
* context
) {
318 int32_t rv
= state_manager_
.CheckOperationState(
319 FileIOStateManager::OPERATION_EXCLUSIVE
, true);
323 if (!base::FileUtilProxy::Flush(
326 base::Bind(&PepperFileIOHost::ExecutePlatformGeneralCallback
,
327 weak_factory_
.GetWeakPtr(),
328 context
->MakeReplyMessageContext())))
329 return PP_ERROR_FAILED
;
331 state_manager_
.SetPendingOperation(FileIOStateManager::OPERATION_EXCLUSIVE
);
332 return PP_OK_COMPLETIONPENDING
;
335 int32_t PepperFileIOHost::OnHostMsgClose(
336 ppapi::host::HostMessageContext
* context
,
337 int64_t max_written_offset
) {
339 file_system_host_
->CloseQuotaFile(this, max_written_offset
);
340 check_quota_
= false;
343 if (file_
!= base::kInvalidPlatformFileValue
) {
344 base::FileUtilProxy::Close(
347 base::Bind(&PepperFileIOHost::DidCloseFile
,
348 weak_factory_
.GetWeakPtr()));
349 file_
= base::kInvalidPlatformFileValue
;
354 void PepperFileIOHost::DidOpenQuotaFile(
355 ppapi::host::ReplyMessageContext reply_context
,
356 base::PlatformFile file
,
357 int64_t max_written_offset
) {
358 max_written_offset_
= max_written_offset
;
360 ExecutePlatformOpenFileCallback(
361 reply_context
, base::PLATFORM_FILE_OK
, base::PassPlatformFile(&file
),
365 void PepperFileIOHost::DidCloseFile(base::PlatformFileError error
) {
366 // Silently ignore if we fail to close the file.
367 if (!on_close_callback_
.is_null()) {
368 on_close_callback_
.Run();
369 on_close_callback_
.Reset();
373 int32_t PepperFileIOHost::OnHostMsgRequestOSFileHandle(
374 ppapi::host::HostMessageContext
* context
) {
375 if (open_flags_
!= PP_FILEOPENFLAG_READ
&& file_system_host_
->ChecksQuota())
376 return PP_ERROR_FAILED
;
379 browser_ppapi_host_
->GetDocumentURLForInstance(pp_instance());
380 BrowserThread::PostTaskAndReplyWithResult(
383 base::Bind(&GetPluginAllowedToCallRequestOSFileHandle
,
386 base::Bind(&PepperFileIOHost::GotPluginAllowedToCallRequestOSFileHandle
,
387 weak_factory_
.GetWeakPtr(),
388 context
->MakeReplyMessageContext()));
389 return PP_OK_COMPLETIONPENDING
;
392 void PepperFileIOHost::GotPluginAllowedToCallRequestOSFileHandle(
393 ppapi::host::ReplyMessageContext reply_context
,
394 bool plugin_allowed
) {
395 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO
));
396 if (!browser_ppapi_host_
->external_plugin() ||
397 host()->permissions().HasPermission(ppapi::PERMISSION_PRIVATE
) ||
399 if (!AddFileToReplyContext(open_flags_
, &reply_context
))
400 reply_context
.params
.set_result(PP_ERROR_FAILED
);
402 reply_context
.params
.set_result(PP_ERROR_NOACCESS
);
404 host()->SendReply(reply_context
,
405 PpapiPluginMsg_FileIO_RequestOSFileHandleReply());
408 void PepperFileIOHost::ExecutePlatformGeneralCallback(
409 ppapi::host::ReplyMessageContext reply_context
,
410 base::PlatformFileError error_code
) {
411 reply_context
.params
.set_result(
412 ppapi::PlatformFileErrorToPepperError(error_code
));
413 host()->SendReply(reply_context
, PpapiPluginMsg_FileIO_GeneralReply());
414 state_manager_
.SetOperationFinished();
417 void PepperFileIOHost::ExecutePlatformOpenFileCallback(
418 ppapi::host::ReplyMessageContext reply_context
,
419 base::PlatformFileError error_code
,
420 base::PassPlatformFile file
,
421 bool unused_created
) {
422 int32_t pp_error
= ppapi::PlatformFileErrorToPepperError(error_code
);
423 DCHECK(file_
== base::kInvalidPlatformFileValue
);
424 file_
= file
.ReleaseValue();
426 if (file_
!= base::kInvalidPlatformFileValue
&&
427 !AddFileToReplyContext(open_flags_
, &reply_context
))
428 pp_error
= PP_ERROR_FAILED
;
430 PP_Resource quota_file_system
= 0;
431 if (pp_error
== PP_OK
) {
432 state_manager_
.SetOpenSucceed();
433 // A non-zero resource id signals the plugin side to check quota.
435 quota_file_system
= file_system_host_
->pp_resource();
438 reply_context
.params
.set_result(pp_error
);
439 host()->SendReply(reply_context
,
440 PpapiPluginMsg_FileIO_OpenReply(quota_file_system
,
441 max_written_offset_
));
442 state_manager_
.SetOperationFinished();
445 void PepperFileIOHost::SendOpenErrorReply(
446 ppapi::host::ReplyMessageContext reply_context
) {
447 host()->SendReply(reply_context
, PpapiPluginMsg_FileIO_OpenReply(0, 0));
450 bool PepperFileIOHost::AddFileToReplyContext(
452 ppapi::host::ReplyMessageContext
* reply_context
) const {
453 base::ProcessId plugin_process_id
=
454 base::GetProcId(browser_ppapi_host_
->GetPluginProcessHandle());
455 if (plugin_process_id
== base::kNullProcessId
)
456 plugin_process_id
= resolved_render_process_id_
;
458 IPC::PlatformFileForTransit transit_file
= BrokerGetFileHandleForProcess(
459 file_
, plugin_process_id
, false);
460 if (transit_file
== IPC::InvalidPlatformFileForTransit())
463 ppapi::proxy::SerializedHandle file_handle
;
464 // A non-zero resource id signals NaClIPCAdapter to create a NaClQuotaDesc.
465 PP_Resource quota_file_io
= check_quota_
? pp_resource() : 0;
466 file_handle
.set_file_handle(transit_file
, open_flags
, quota_file_io
);
467 reply_context
->params
.AppendHandle(file_handle
);
471 } // namespace content