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 "storage/browser/fileapi/file_observers.h"
32 #include "storage/browser/fileapi/file_system_context.h"
33 #include "storage/browser/fileapi/file_system_operation_runner.h"
34 #include "storage/browser/fileapi/task_runner_bound_observer_list.h"
35 #include "storage/common/fileapi/file_system_util.h"
39 using ppapi::FileIOStateManager
;
40 using ppapi::PPTimeToTime
;
44 PepperFileIOHost::UIThreadStuff
GetUIThreadStuffForInternalFileSystems(
45 int render_process_id
) {
46 PepperFileIOHost::UIThreadStuff stuff
;
47 DCHECK_CURRENTLY_ON(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_CURRENTLY_ON(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_CURRENTLY_ON(BrowserThread::UI
);
69 ContentBrowserClient
* client
= GetContentClient()->browser();
70 RenderProcessHost
* host
= RenderProcessHost::FromID(render_process_id
);
73 return client
->IsPluginAllowedToCallRequestOSFileHandle(
74 host
->GetBrowserContext(), document_url
);
77 bool FileOpenForWrite(int32_t open_flags
) {
78 return (open_flags
& (PP_FILEOPENFLAG_WRITE
| PP_FILEOPENFLAG_APPEND
)) != 0;
81 void FileCloser(base::File auto_close
) {
84 void DidCloseFile(const base::Closure
& on_close_callback
) {
85 if (!on_close_callback
.is_null())
86 on_close_callback
.Run();
89 void DidOpenFile(base::WeakPtr
<PepperFileIOHost
> file_host
,
90 storage::FileSystemOperation::OpenFileCallback callback
,
92 const base::Closure
& on_close_callback
) {
94 callback
.Run(file
.Pass(), on_close_callback
);
96 BrowserThread::PostTaskAndReply(
99 base::Bind(&FileCloser
, base::Passed(&file
)),
100 base::Bind(&DidCloseFile
, on_close_callback
));
106 PepperFileIOHost::PepperFileIOHost(BrowserPpapiHostImpl
* host
,
107 PP_Instance instance
,
108 PP_Resource resource
)
109 : ResourceHost(host
->GetPpapiHost(), instance
, resource
),
110 browser_ppapi_host_(host
),
111 render_process_host_(NULL
),
112 file_(BrowserThread::GetMessageLoopProxyForThread(BrowserThread::FILE)
115 file_system_type_(PP_FILESYSTEMTYPE_INVALID
),
116 max_written_offset_(0),
117 check_quota_(false) {
119 if (!host
->GetRenderFrameIDsForInstance(
120 instance
, &render_process_id_
, &unused
)) {
121 render_process_id_
= -1;
125 PepperFileIOHost::~PepperFileIOHost() {}
127 int32_t PepperFileIOHost::OnResourceMessageReceived(
128 const IPC::Message
& msg
,
129 ppapi::host::HostMessageContext
* context
) {
130 PPAPI_BEGIN_MESSAGE_MAP(PepperFileIOHost
, msg
)
131 PPAPI_DISPATCH_HOST_RESOURCE_CALL(PpapiHostMsg_FileIO_Open
, OnHostMsgOpen
)
132 PPAPI_DISPATCH_HOST_RESOURCE_CALL(PpapiHostMsg_FileIO_Touch
, OnHostMsgTouch
)
133 PPAPI_DISPATCH_HOST_RESOURCE_CALL(PpapiHostMsg_FileIO_SetLength
,
135 PPAPI_DISPATCH_HOST_RESOURCE_CALL_0(PpapiHostMsg_FileIO_Flush
,
137 PPAPI_DISPATCH_HOST_RESOURCE_CALL(PpapiHostMsg_FileIO_Close
, OnHostMsgClose
)
138 PPAPI_DISPATCH_HOST_RESOURCE_CALL_0(PpapiHostMsg_FileIO_RequestOSFileHandle
,
139 OnHostMsgRequestOSFileHandle
)
140 PPAPI_END_MESSAGE_MAP()
141 return PP_ERROR_FAILED
;
144 PepperFileIOHost::UIThreadStuff::UIThreadStuff() {
145 resolved_render_process_id
= base::kNullProcessId
;
148 PepperFileIOHost::UIThreadStuff::~UIThreadStuff() {}
150 int32_t PepperFileIOHost::OnHostMsgOpen(
151 ppapi::host::HostMessageContext
* context
,
152 PP_Resource file_ref_resource
,
153 int32_t open_flags
) {
154 int32_t rv
= state_manager_
.CheckOperationState(
155 FileIOStateManager::OPERATION_EXCLUSIVE
, false);
159 int platform_file_flags
= 0;
160 if (!ppapi::PepperFileOpenFlagsToPlatformFileFlags(open_flags
,
161 &platform_file_flags
))
162 return PP_ERROR_BADARGUMENT
;
164 ppapi::host::ResourceHost
* resource_host
=
165 host()->GetResourceHost(file_ref_resource
);
166 if (!resource_host
|| !resource_host
->IsFileRefHost())
167 return PP_ERROR_BADRESOURCE
;
168 PepperFileRefHost
* file_ref_host
=
169 static_cast<PepperFileRefHost
*>(resource_host
);
170 if (file_ref_host
->GetFileSystemType() == PP_FILESYSTEMTYPE_INVALID
)
171 return PP_ERROR_FAILED
;
173 file_system_host_
= file_ref_host
->GetFileSystemHost();
175 open_flags_
= open_flags
;
176 file_system_type_
= file_ref_host
->GetFileSystemType();
177 file_system_url_
= file_ref_host
->GetFileSystemURL();
179 // For external file systems, if there is a valid FileSystemURL, then treat
180 // it like internal file systems and access it via the FileSystemURL.
181 bool is_internal_type
= (file_system_type_
!= PP_FILESYSTEMTYPE_EXTERNAL
) ||
182 file_system_url_
.is_valid();
184 if (is_internal_type
) {
185 if (!file_system_url_
.is_valid())
186 return PP_ERROR_BADARGUMENT
;
188 // Not all external file systems are fully supported yet.
189 // Whitelist the supported ones.
190 if (file_system_url_
.mount_type() == storage::kFileSystemTypeExternal
) {
191 switch (file_system_url_
.type()) {
192 case storage::kFileSystemTypeNativeMedia
:
193 case storage::kFileSystemTypeDeviceMedia
:
194 case storage::kFileSystemTypePicasa
:
195 case storage::kFileSystemTypeItunes
:
196 case storage::kFileSystemTypeIphoto
:
199 return PP_ERROR_NOACCESS
;
202 if (!CanOpenFileSystemURLWithPepperFlags(
203 open_flags
, render_process_id_
, file_system_url_
))
204 return PP_ERROR_NOACCESS
;
205 BrowserThread::PostTaskAndReplyWithResult(
208 base::Bind(&GetUIThreadStuffForInternalFileSystems
, render_process_id_
),
209 base::Bind(&PepperFileIOHost::GotUIThreadStuffForInternalFileSystems
,
211 context
->MakeReplyMessageContext(),
212 platform_file_flags
));
214 base::FilePath path
= file_ref_host
->GetExternalFilePath();
215 if (!CanOpenWithPepperFlags(open_flags
, render_process_id_
, path
))
216 return PP_ERROR_NOACCESS
;
217 BrowserThread::PostTaskAndReplyWithResult(
220 base::Bind(&GetResolvedRenderProcessId
, render_process_id_
),
221 base::Bind(&PepperFileIOHost::GotResolvedRenderProcessId
,
223 context
->MakeReplyMessageContext(),
225 platform_file_flags
));
227 state_manager_
.SetPendingOperation(FileIOStateManager::OPERATION_EXCLUSIVE
);
228 return PP_OK_COMPLETIONPENDING
;
231 void PepperFileIOHost::GotUIThreadStuffForInternalFileSystems(
232 ppapi::host::ReplyMessageContext reply_context
,
233 int platform_file_flags
,
234 UIThreadStuff ui_thread_stuff
) {
235 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
236 file_system_context_
= ui_thread_stuff
.file_system_context
;
237 resolved_render_process_id_
= ui_thread_stuff
.resolved_render_process_id
;
238 if (resolved_render_process_id_
== base::kNullProcessId
||
239 !file_system_context_
.get()) {
240 reply_context
.params
.set_result(PP_ERROR_FAILED
);
241 SendOpenErrorReply(reply_context
);
245 if (!file_system_context_
->GetFileSystemBackend(file_system_url_
.type())) {
246 reply_context
.params
.set_result(PP_ERROR_FAILED
);
247 SendOpenErrorReply(reply_context
);
251 DCHECK(file_system_host_
.get());
252 DCHECK(file_system_host_
->GetFileSystemOperationRunner());
254 file_system_host_
->GetFileSystemOperationRunner()->OpenFile(
257 base::Bind(&DidOpenFile
,
259 base::Bind(&PepperFileIOHost::DidOpenInternalFile
,
264 void PepperFileIOHost::DidOpenInternalFile(
265 ppapi::host::ReplyMessageContext reply_context
,
267 const base::Closure
& on_close_callback
) {
268 if (file
.IsValid()) {
269 on_close_callback_
= on_close_callback
;
271 if (FileOpenForWrite(open_flags_
) && file_system_host_
->ChecksQuota()) {
273 file_system_host_
->OpenQuotaFile(
276 base::Bind(&PepperFileIOHost::DidOpenQuotaFile
,
279 base::Passed(&file
)));
284 DCHECK(!file_
.IsValid());
285 base::File::Error error
=
286 file
.IsValid() ? base::File::FILE_OK
: file
.error_details();
287 file_
.SetFile(file
.Pass());
288 OnOpenProxyCallback(reply_context
, error
);
291 void PepperFileIOHost::GotResolvedRenderProcessId(
292 ppapi::host::ReplyMessageContext reply_context
,
295 base::ProcessId resolved_render_process_id
) {
296 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
297 resolved_render_process_id_
= resolved_render_process_id
;
301 base::Bind(&PepperFileIOHost::OnOpenProxyCallback
,
306 int32_t PepperFileIOHost::OnHostMsgTouch(
307 ppapi::host::HostMessageContext
* context
,
308 PP_Time last_access_time
,
309 PP_Time last_modified_time
) {
310 int32_t rv
= state_manager_
.CheckOperationState(
311 FileIOStateManager::OPERATION_EXCLUSIVE
, true);
316 PPTimeToTime(last_access_time
),
317 PPTimeToTime(last_modified_time
),
318 base::Bind(&PepperFileIOHost::ExecutePlatformGeneralCallback
,
320 context
->MakeReplyMessageContext()))) {
321 return PP_ERROR_FAILED
;
324 state_manager_
.SetPendingOperation(FileIOStateManager::OPERATION_EXCLUSIVE
);
325 return PP_OK_COMPLETIONPENDING
;
328 int32_t PepperFileIOHost::OnHostMsgSetLength(
329 ppapi::host::HostMessageContext
* context
,
331 int32_t rv
= state_manager_
.CheckOperationState(
332 FileIOStateManager::OPERATION_EXCLUSIVE
, true);
336 return PP_ERROR_BADARGUMENT
;
338 // Quota checks are performed on the plugin side, in order to use the same
339 // quota reservation and request system as Write.
341 if (!file_
.SetLength(
343 base::Bind(&PepperFileIOHost::ExecutePlatformGeneralCallback
,
345 context
->MakeReplyMessageContext()))) {
346 return PP_ERROR_FAILED
;
349 state_manager_
.SetPendingOperation(FileIOStateManager::OPERATION_EXCLUSIVE
);
350 return PP_OK_COMPLETIONPENDING
;
353 int32_t PepperFileIOHost::OnHostMsgFlush(
354 ppapi::host::HostMessageContext
* context
) {
355 int32_t rv
= state_manager_
.CheckOperationState(
356 FileIOStateManager::OPERATION_EXCLUSIVE
, true);
361 base::Bind(&PepperFileIOHost::ExecutePlatformGeneralCallback
,
363 context
->MakeReplyMessageContext()))) {
364 return PP_ERROR_FAILED
;
367 state_manager_
.SetPendingOperation(FileIOStateManager::OPERATION_EXCLUSIVE
);
368 return PP_OK_COMPLETIONPENDING
;
371 int32_t PepperFileIOHost::OnHostMsgClose(
372 ppapi::host::HostMessageContext
* context
,
373 const ppapi::FileGrowth
& file_growth
) {
375 file_system_host_
->CloseQuotaFile(this, file_growth
);
376 check_quota_
= false;
379 if (file_
.IsValid()) {
380 file_
.Close(base::Bind(&PepperFileIOHost::DidCloseFile
,
386 void PepperFileIOHost::DidOpenQuotaFile(
387 ppapi::host::ReplyMessageContext reply_context
,
389 int64_t max_written_offset
) {
390 DCHECK(!file_
.IsValid());
391 DCHECK(file
.IsValid());
392 max_written_offset_
= max_written_offset
;
393 file_
.SetFile(file
.Pass());
395 OnOpenProxyCallback(reply_context
, base::File::FILE_OK
);
398 void PepperFileIOHost::DidCloseFile(base::File::Error
/*error*/) {
399 // Silently ignore if we fail to close the file.
400 if (!on_close_callback_
.is_null()) {
401 on_close_callback_
.Run();
402 on_close_callback_
.Reset();
406 int32_t PepperFileIOHost::OnHostMsgRequestOSFileHandle(
407 ppapi::host::HostMessageContext
* context
) {
408 if (open_flags_
!= PP_FILEOPENFLAG_READ
&& file_system_host_
->ChecksQuota())
409 return PP_ERROR_FAILED
;
412 browser_ppapi_host_
->GetDocumentURLForInstance(pp_instance());
413 BrowserThread::PostTaskAndReplyWithResult(
416 base::Bind(&GetPluginAllowedToCallRequestOSFileHandle
,
419 base::Bind(&PepperFileIOHost::GotPluginAllowedToCallRequestOSFileHandle
,
421 context
->MakeReplyMessageContext()));
422 return PP_OK_COMPLETIONPENDING
;
425 void PepperFileIOHost::GotPluginAllowedToCallRequestOSFileHandle(
426 ppapi::host::ReplyMessageContext reply_context
,
427 bool plugin_allowed
) {
428 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
429 if (!browser_ppapi_host_
->external_plugin() ||
430 host()->permissions().HasPermission(ppapi::PERMISSION_PRIVATE
) ||
432 if (!AddFileToReplyContext(open_flags_
, &reply_context
))
433 reply_context
.params
.set_result(PP_ERROR_FAILED
);
435 reply_context
.params
.set_result(PP_ERROR_NOACCESS
);
437 host()->SendReply(reply_context
,
438 PpapiPluginMsg_FileIO_RequestOSFileHandleReply());
441 void PepperFileIOHost::ExecutePlatformGeneralCallback(
442 ppapi::host::ReplyMessageContext reply_context
,
443 base::File::Error error_code
) {
444 reply_context
.params
.set_result(ppapi::FileErrorToPepperError(error_code
));
445 host()->SendReply(reply_context
, PpapiPluginMsg_FileIO_GeneralReply());
446 state_manager_
.SetOperationFinished();
449 void PepperFileIOHost::OnOpenProxyCallback(
450 ppapi::host::ReplyMessageContext reply_context
,
451 base::File::Error error_code
) {
452 int32_t pp_error
= ppapi::FileErrorToPepperError(error_code
);
453 if (file_
.IsValid() && !AddFileToReplyContext(open_flags_
, &reply_context
))
454 pp_error
= PP_ERROR_FAILED
;
456 PP_Resource quota_file_system
= 0;
457 if (pp_error
== PP_OK
) {
458 state_manager_
.SetOpenSucceed();
459 // A non-zero resource id signals the plugin side to check quota.
461 quota_file_system
= file_system_host_
->pp_resource();
464 reply_context
.params
.set_result(pp_error
);
467 PpapiPluginMsg_FileIO_OpenReply(quota_file_system
, max_written_offset_
));
468 state_manager_
.SetOperationFinished();
471 void PepperFileIOHost::SendOpenErrorReply(
472 ppapi::host::ReplyMessageContext reply_context
) {
473 host()->SendReply(reply_context
, PpapiPluginMsg_FileIO_OpenReply(0, 0));
476 bool PepperFileIOHost::AddFileToReplyContext(
478 ppapi::host::ReplyMessageContext
* reply_context
) const {
479 base::ProcessId plugin_process_id
=
480 base::GetProcId(browser_ppapi_host_
->GetPluginProcess().Handle());
481 if (plugin_process_id
== base::kNullProcessId
)
482 plugin_process_id
= resolved_render_process_id_
;
484 IPC::PlatformFileForTransit transit_file
=
485 BrokerGetFileHandleForProcess(file_
.GetPlatformFile(), plugin_process_id
,
487 if (transit_file
== IPC::InvalidPlatformFileForTransit())
490 ppapi::proxy::SerializedHandle file_handle
;
491 // A non-zero resource id signals NaClIPCAdapter to create a NaClQuotaDesc.
492 PP_Resource quota_file_io
= check_quota_
? pp_resource() : 0;
493 file_handle
.set_file_handle(transit_file
, open_flags
, quota_file_io
);
494 reply_context
->params
.AppendHandle(file_handle
);
498 } // namespace content