1 // Copyright 2013 The Chromium Authors
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"
10 #include "base/callback.h"
11 #include "base/logging.h"
12 #include "base/callback_forward.h"
13 #include "base/callback_helpers.h"
14 #include "base/memory/weak_ptr.h"
15 #include "base/task/sequenced_task_runner.h"
16 #include "base/task/thread_pool.h"
17 #include "build/build_config.h"
18 #include "content/browser/renderer_host/pepper/pepper_file_ref_host.h"
19 #include "content/browser/renderer_host/pepper/pepper_file_system_browser_host.h"
20 #include "content/browser/renderer_host/pepper/pepper_security_helper.h"
21 #include "content/public/browser/browser_task_traits.h"
22 #include "content/public/browser/browser_thread.h"
23 #include "content/public/browser/content_browser_client.h"
24 #include "content/public/browser/render_process_host.h"
25 #include "content/public/browser/storage_partition.h"
26 #include "content/public/common/content_client.h"
27 #include "ipc/ipc_platform_file.h"
28 #include "mojo/public/cpp/bindings/callback_helpers.h"
29 #include "ppapi/c/pp_errors.h"
30 #include "ppapi/c/ppb_file_io.h"
31 #include "ppapi/host/dispatch_host_message.h"
32 #include "ppapi/host/ppapi_host.h"
33 #include "ppapi/proxy/ppapi_messages.h"
34 #include "ppapi/shared_impl/file_system_util.h"
35 #include "ppapi/shared_impl/file_type_conversion.h"
36 #include "ppapi/shared_impl/time_conversion.h"
37 #include "storage/browser/file_system/file_observers.h"
38 #include "storage/browser/file_system/file_system_context.h"
39 #include "storage/browser/file_system/file_system_operation_runner.h"
40 #include "storage/browser/file_system/task_runner_bound_observer_list.h"
41 #include "storage/common/file_system/file_system_util.h"
45 using ppapi::FileIOStateManager;
46 using ppapi::PPTimeToTime;
50 PepperFileIOHost::UIThreadStuff GetUIThreadStuffForInternalFileSystems(
51 int render_process_id) {
52 PepperFileIOHost::UIThreadStuff stuff;
53 DCHECK_CURRENTLY_ON(BrowserThread::UI);
54 RenderProcessHost* host = RenderProcessHost::FromID(render_process_id);
56 stuff.resolved_render_process_id = host->GetProcess().Pid();
57 StoragePartition* storage_partition = host->GetStoragePartition();
58 if (storage_partition)
59 stuff.file_system_context = storage_partition->GetFileSystemContext();
64 base::ProcessId GetResolvedRenderProcessId(int render_process_id) {
65 DCHECK_CURRENTLY_ON(BrowserThread::UI);
66 RenderProcessHost* host = RenderProcessHost::FromID(render_process_id);
68 return base::kNullProcessId;
69 return host->GetProcess().Pid();
72 bool GetPluginAllowedToCallRequestOSFileHandle(int render_process_id,
73 const GURL& document_url) {
74 DCHECK_CURRENTLY_ON(BrowserThread::UI);
75 ContentBrowserClient* client = GetContentClient()->browser();
76 RenderProcessHost* host = RenderProcessHost::FromID(render_process_id);
79 return client->IsPluginAllowedToCallRequestOSFileHandle(
80 host->GetBrowserContext(), document_url);
83 bool FileOpenForWrite(int32_t open_flags) {
84 return (open_flags & (PP_FILEOPENFLAG_WRITE | PP_FILEOPENFLAG_APPEND)) != 0;
87 void FileCloser(base::File auto_close) {
90 void DidCloseFile(base::ScopedClosureRunner on_close_callback) {
91 on_close_callback.RunAndReset();
94 void DidOpenFile(base::WeakPtr<PepperFileIOHost> file_host,
95 scoped_refptr<base::SequencedTaskRunner> task_runner,
96 storage::FileSystemOperation::OpenFileCallback callback,
98 base::ScopedClosureRunner on_close_callback) {
100 std::move(callback).Run(std::move(file), on_close_callback.Release());
102 task_runner->PostTaskAndReply(
103 FROM_HERE, base::BindOnce(&FileCloser, std::move(file)),
104 base::BindOnce(&DidCloseFile, std::move(on_close_callback)));
108 void OpenFileCallbackWrapperIO(
109 storage::FileSystemOperationRunner::OpenFileCallback callback,
111 base::ScopedClosureRunner on_close_callback) {
112 GetUIThreadTaskRunner({})->PostTask(
113 FROM_HERE, base::BindOnce(std::move(callback), std::move(file),
114 std::move(on_close_callback)));
118 PepperFileSystemBrowserHost::GetOperationRunnerCallback get_runner,
119 const storage::FileSystemURL& url,
121 storage::FileSystemOperationRunner::OpenFileCallback callback) {
122 get_runner.Run()->OpenFile(
124 base::BindOnce(&OpenFileCallbackWrapperIO, std::move(callback)));
129 PepperFileIOHost::PepperFileIOHost(BrowserPpapiHostImpl* host,
130 PP_Instance instance,
131 PP_Resource resource)
132 : ResourceHost(host->GetPpapiHost(), instance, resource),
133 browser_ppapi_host_(host),
134 task_runner_(base::ThreadPool::CreateSequencedTaskRunner(
135 {base::MayBlock(), base::TaskPriority::USER_VISIBLE,
136 base::TaskShutdownBehavior::BLOCK_SHUTDOWN})),
137 file_(task_runner_.get()),
138 resolved_render_process_id_(base::kNullProcessId),
140 file_system_type_(PP_FILESYSTEMTYPE_INVALID),
141 max_written_offset_(0),
142 check_quota_(false) {
144 if (!host->GetRenderFrameIDsForInstance(
145 instance, &render_process_id_, &unused)) {
146 render_process_id_ = -1;
150 PepperFileIOHost::~PepperFileIOHost() {}
152 int32_t PepperFileIOHost::OnResourceMessageReceived(
153 const IPC::Message& msg,
154 ppapi::host::HostMessageContext* context) {
155 PPAPI_BEGIN_MESSAGE_MAP(PepperFileIOHost, msg)
156 PPAPI_DISPATCH_HOST_RESOURCE_CALL(PpapiHostMsg_FileIO_Open, OnHostMsgOpen)
157 PPAPI_DISPATCH_HOST_RESOURCE_CALL(PpapiHostMsg_FileIO_Touch, OnHostMsgTouch)
158 PPAPI_DISPATCH_HOST_RESOURCE_CALL(PpapiHostMsg_FileIO_SetLength,
160 PPAPI_DISPATCH_HOST_RESOURCE_CALL_0(PpapiHostMsg_FileIO_Flush,
162 PPAPI_DISPATCH_HOST_RESOURCE_CALL(PpapiHostMsg_FileIO_Close, OnHostMsgClose)
163 PPAPI_DISPATCH_HOST_RESOURCE_CALL_0(PpapiHostMsg_FileIO_RequestOSFileHandle,
164 OnHostMsgRequestOSFileHandle)
165 PPAPI_END_MESSAGE_MAP()
166 LOG(ERROR) << "Message not resolved";
167 return PP_ERROR_FAILED;
170 PepperFileIOHost::UIThreadStuff::UIThreadStuff() {
171 resolved_render_process_id = base::kNullProcessId;
174 PepperFileIOHost::UIThreadStuff::UIThreadStuff(const UIThreadStuff& other) =
177 PepperFileIOHost::UIThreadStuff::~UIThreadStuff() {}
179 int32_t PepperFileIOHost::OnHostMsgOpen(
180 ppapi::host::HostMessageContext* context,
181 PP_Resource file_ref_resource,
182 int32_t open_flags) {
183 int32_t rv = state_manager_.CheckOperationState(
184 FileIOStateManager::OPERATION_EXCLUSIVE, false);
188 uint32_t platform_file_flags = 0;
189 if (!ppapi::PepperFileOpenFlagsToPlatformFileFlags(open_flags,
190 &platform_file_flags)) {
192 return PP_ERROR_BADARGUMENT;
195 ppapi::host::ResourceHost* resource_host =
196 host()->GetResourceHost(file_ref_resource);
197 if (!resource_host || !resource_host->IsFileRefHost()) {
198 LOG(ERROR) << "Invalid Resource";
199 return PP_ERROR_BADRESOURCE;
201 PepperFileRefHost* file_ref_host =
202 static_cast<PepperFileRefHost*>(resource_host);
203 if (file_ref_host->GetFileSystemType() == PP_FILESYSTEMTYPE_INVALID) {
204 LOG(ERROR) << "Invalid FileSystem type";
205 return PP_ERROR_FAILED;
208 file_system_host_ = file_ref_host->GetFileSystemHost();
210 open_flags_ = open_flags;
211 file_system_type_ = file_ref_host->GetFileSystemType();
212 file_system_url_ = file_ref_host->GetFileSystemURL();
214 // For external file systems, if there is a valid FileSystemURL, then treat
215 // it like internal file systems and access it via the FileSystemURL.
216 bool is_internal_type = (file_system_type_ != PP_FILESYSTEMTYPE_EXTERNAL) ||
217 file_system_url_.is_valid();
219 if (is_internal_type) {
220 if (!file_system_url_.is_valid()) {
221 LOG(ERROR) << "Invalid URL";
222 return PP_ERROR_BADARGUMENT;
225 // Not all external file systems are fully supported yet.
226 // Whitelist the supported ones.
227 if (file_system_url_.mount_type() == storage::kFileSystemTypeExternal) {
228 switch (file_system_url_.type()) {
229 case storage::kFileSystemTypeLocalMedia:
230 case storage::kFileSystemTypeDeviceMedia:
233 LOG(ERROR) << "External file system of type "
234 << file_system_url_.type() << " not supported";
235 return PP_ERROR_NOACCESS;
238 if (!CanOpenFileSystemURLWithPepperFlags(open_flags, render_process_id_,
240 LOG(ERROR) << "Cannot open file system URL with pepper flags";
241 return PP_ERROR_NOACCESS;
244 GotUIThreadStuffForInternalFileSystems(
245 context->MakeReplyMessageContext(), platform_file_flags,
246 GetUIThreadStuffForInternalFileSystems(render_process_id_));
248 base::FilePath path = file_ref_host->GetExternalFilePath();
249 if (!CanOpenWithPepperFlags(open_flags, render_process_id_, path)) {
250 LOG(ERROR) << "Cannot open with pepper flags";
251 return PP_ERROR_NOACCESS;
253 GetUIThreadTaskRunner({})->PostTaskAndReplyWithResult(
255 base::BindOnce(&GetResolvedRenderProcessId, render_process_id_),
256 base::BindOnce(&PepperFileIOHost::GotResolvedRenderProcessId,
257 AsWeakPtr(), context->MakeReplyMessageContext(), path,
258 platform_file_flags));
260 state_manager_.SetPendingOperation(FileIOStateManager::OPERATION_EXCLUSIVE);
261 return PP_OK_COMPLETIONPENDING;
264 void PepperFileIOHost::GotUIThreadStuffForInternalFileSystems(
265 ppapi::host::ReplyMessageContext reply_context,
266 uint32_t platform_file_flags,
267 UIThreadStuff ui_thread_stuff) {
268 DCHECK_CURRENTLY_ON(BrowserThread::UI);
269 resolved_render_process_id_ = ui_thread_stuff.resolved_render_process_id;
270 if (resolved_render_process_id_ == base::kNullProcessId ||
271 !ui_thread_stuff.file_system_context.get()) {
272 reply_context.params.set_result(PP_ERROR_FAILED);
273 SendOpenErrorReply(reply_context);
277 if (!ui_thread_stuff.file_system_context->GetFileSystemBackend(
278 file_system_url_.type())) {
279 reply_context.params.set_result(PP_ERROR_FAILED);
280 SendOpenErrorReply(reply_context);
284 if (!file_system_host_.get()) {
285 reply_context.params.set_result(PP_ERROR_FAILED);
286 SendOpenErrorReply(reply_context);
291 base::BindOnce(&DidOpenFile, AsWeakPtr(), task_runner_,
292 base::BindOnce(&PepperFileIOHost::DidOpenInternalFile,
293 AsWeakPtr(), reply_context));
294 GetIOThreadTaskRunner({})->PostTask(
297 CallOpenFile, file_system_host_->GetFileSystemOperationRunner(),
298 file_system_url_, platform_file_flags, std::move(open_callback)));
301 void PepperFileIOHost::DidOpenInternalFile(
302 ppapi::host::ReplyMessageContext reply_context,
304 base::OnceClosure on_close_callback) {
305 if (file.IsValid()) {
306 base::ScopedClosureRunner scoped_runner(std::move(on_close_callback));
307 on_close_callback_ = std::move(scoped_runner);
309 if (FileOpenForWrite(open_flags_) && file_system_host_->ChecksQuota()) {
311 file_system_host_->OpenQuotaFile(
312 this, file_system_url_,
313 base::BindOnce(&PepperFileIOHost::DidOpenQuotaFile, AsWeakPtr(),
314 reply_context, std::move(file)));
319 DCHECK(!file_.IsValid());
320 base::File::Error error =
321 file.IsValid() ? base::File::FILE_OK : file.error_details();
322 file_.SetFile(std::move(file));
323 SendFileOpenReply(reply_context, error);
326 void PepperFileIOHost::GotResolvedRenderProcessId(
327 ppapi::host::ReplyMessageContext reply_context,
330 base::ProcessId resolved_render_process_id) {
331 DCHECK_CURRENTLY_ON(BrowserThread::UI);
332 resolved_render_process_id_ = resolved_render_process_id;
333 file_.CreateOrOpen(path, file_flags,
334 base::BindOnce(&PepperFileIOHost::OnLocalFileOpened,
335 AsWeakPtr(), reply_context, path));
338 int32_t PepperFileIOHost::OnHostMsgTouch(
339 ppapi::host::HostMessageContext* context,
340 PP_Time last_access_time,
341 PP_Time last_modified_time) {
342 int32_t rv = state_manager_.CheckOperationState(
343 FileIOStateManager::OPERATION_EXCLUSIVE, true);
348 PPTimeToTime(last_access_time), PPTimeToTime(last_modified_time),
349 base::BindOnce(&PepperFileIOHost::ExecutePlatformGeneralCallback,
350 AsWeakPtr(), context->MakeReplyMessageContext()))) {
351 LOG(ERROR) << "Could not modify file access and modification times";
352 return PP_ERROR_FAILED;
355 state_manager_.SetPendingOperation(FileIOStateManager::OPERATION_EXCLUSIVE);
356 return PP_OK_COMPLETIONPENDING;
359 int32_t PepperFileIOHost::OnHostMsgSetLength(
360 ppapi::host::HostMessageContext* context,
362 int32_t rv = state_manager_.CheckOperationState(
363 FileIOStateManager::OPERATION_EXCLUSIVE, true);
367 LOG(ERROR) << "New length cannot be less than 0, length=" << length;
368 return PP_ERROR_BADARGUMENT;
371 // Quota checks are performed on the plugin side, in order to use the same
372 // quota reservation and request system as Write.
374 if (!file_.SetLength(
376 base::BindOnce(&PepperFileIOHost::ExecutePlatformGeneralCallback,
377 AsWeakPtr(), context->MakeReplyMessageContext()))) {
378 LOG(ERROR) << "SetLength failed";
379 return PP_ERROR_FAILED;
382 state_manager_.SetPendingOperation(FileIOStateManager::OPERATION_EXCLUSIVE);
383 return PP_OK_COMPLETIONPENDING;
386 int32_t PepperFileIOHost::OnHostMsgFlush(
387 ppapi::host::HostMessageContext* context) {
388 int32_t rv = state_manager_.CheckOperationState(
389 FileIOStateManager::OPERATION_EXCLUSIVE, true);
394 base::BindOnce(&PepperFileIOHost::ExecutePlatformGeneralCallback,
395 AsWeakPtr(), context->MakeReplyMessageContext()))) {
396 LOG(ERROR) << "Flush failed";
397 return PP_ERROR_FAILED;
400 state_manager_.SetPendingOperation(FileIOStateManager::OPERATION_EXCLUSIVE);
401 return PP_OK_COMPLETIONPENDING;
404 int32_t PepperFileIOHost::OnHostMsgClose(
405 ppapi::host::HostMessageContext* context,
406 const ppapi::FileGrowth& file_growth) {
408 file_system_host_->CloseQuotaFile(this, file_growth);
409 check_quota_ = false;
412 if (file_.IsValid()) {
413 file_.Close(base::BindOnce(&PepperFileIOHost::DidCloseFile, AsWeakPtr()));
418 void PepperFileIOHost::DidOpenQuotaFile(
419 ppapi::host::ReplyMessageContext reply_context,
421 int64_t max_written_offset) {
422 DCHECK(!file_.IsValid());
423 DCHECK(file.IsValid());
424 max_written_offset_ = max_written_offset;
425 file_.SetFile(std::move(file));
427 SendFileOpenReply(reply_context, base::File::FILE_OK);
430 void PepperFileIOHost::DidCloseFile(base::File::Error /*error*/) {
431 // Silently ignore if we fail to close the file.
432 on_close_callback_.RunAndReset();
435 int32_t PepperFileIOHost::OnHostMsgRequestOSFileHandle(
436 ppapi::host::HostMessageContext* context) {
437 if (open_flags_ != PP_FILEOPENFLAG_READ && file_system_host_->ChecksQuota()) {
439 return PP_ERROR_FAILED;
443 browser_ppapi_host_->GetDocumentURLForInstance(pp_instance());
444 GetUIThreadTaskRunner({})->PostTaskAndReplyWithResult(
446 base::BindOnce(&GetPluginAllowedToCallRequestOSFileHandle,
447 render_process_id_, document_url),
449 &PepperFileIOHost::GotPluginAllowedToCallRequestOSFileHandle,
450 AsWeakPtr(), context->MakeReplyMessageContext()));
451 return PP_OK_COMPLETIONPENDING;
454 void PepperFileIOHost::GotPluginAllowedToCallRequestOSFileHandle(
455 ppapi::host::ReplyMessageContext reply_context,
456 bool plugin_allowed) {
457 DCHECK_CURRENTLY_ON(BrowserThread::UI);
458 if (!browser_ppapi_host_->external_plugin() ||
459 host()->permissions().HasPermission(ppapi::PERMISSION_PRIVATE) ||
461 if (!AddFileToReplyContext(open_flags_, &reply_context))
462 reply_context.params.set_result(PP_ERROR_FAILED);
464 reply_context.params.set_result(PP_ERROR_NOACCESS);
466 host()->SendReply(reply_context,
467 PpapiPluginMsg_FileIO_RequestOSFileHandleReply());
470 void PepperFileIOHost::ExecutePlatformGeneralCallback(
471 ppapi::host::ReplyMessageContext reply_context,
472 base::File::Error error_code) {
473 reply_context.params.set_result(ppapi::FileErrorToPepperError(error_code));
474 host()->SendReply(reply_context, PpapiPluginMsg_FileIO_GeneralReply());
475 state_manager_.SetOperationFinished();
478 void PepperFileIOHost::OnLocalFileOpened(
479 ppapi::host::ReplyMessageContext reply_context,
480 const base::FilePath& path,
481 base::File::Error error_code) {
482 #if BUILDFLAG(IS_WIN) || BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)
483 // Quarantining a file before its contents are available is only supported on
484 // Windows and Linux.
485 if (!FileOpenForWrite(open_flags_) || error_code != base::File::FILE_OK) {
486 SendFileOpenReply(reply_context, error_code);
490 mojo::Remote<quarantine::mojom::Quarantine> quarantine_remote;
491 download::QuarantineConnectionCallback quarantine_connection_callback =
492 GetContentClient()->browser()->GetQuarantineConnectionCallback();
493 if (quarantine_connection_callback) {
494 quarantine_connection_callback.Run(
495 quarantine_remote.BindNewPipeAndPassReceiver());
498 if (quarantine_remote) {
499 quarantine::mojom::Quarantine* raw_quarantine = quarantine_remote.get();
500 raw_quarantine->QuarantineFile(
501 path, browser_ppapi_host_->GetDocumentURLForInstance(pp_instance()),
502 GURL(), std::string(),
503 mojo::WrapCallbackWithDefaultInvokeIfNotRun(
504 base::BindOnce(&PepperFileIOHost::OnLocalFileQuarantined,
505 AsWeakPtr(), reply_context, path,
506 std::move(quarantine_remote)),
507 quarantine::mojom::QuarantineFileResult::ANNOTATION_FAILED));
509 SendFileOpenReply(reply_context, error_code);
512 SendFileOpenReply(reply_context, error_code);
516 #if BUILDFLAG(IS_WIN) || BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)
517 void PepperFileIOHost::OnLocalFileQuarantined(
518 ppapi::host::ReplyMessageContext reply_context,
519 const base::FilePath& path,
520 mojo::Remote<quarantine::mojom::Quarantine> quarantine_remote,
521 quarantine::mojom::QuarantineFileResult quarantine_result) {
522 base::File::Error file_error =
523 (quarantine_result == quarantine::mojom::QuarantineFileResult::OK
524 ? base::File::FILE_OK
525 : base::File::FILE_ERROR_SECURITY);
526 if (file_error != base::File::FILE_OK && file_.IsValid())
527 file_.Close(base::FileProxy::StatusCallback());
528 SendFileOpenReply(reply_context, file_error);
532 void PepperFileIOHost::SendFileOpenReply(
533 ppapi::host::ReplyMessageContext reply_context,
534 base::File::Error error_code) {
535 int32_t pp_error = ppapi::FileErrorToPepperError(error_code);
536 if (file_.IsValid() && !AddFileToReplyContext(open_flags_, &reply_context))
537 pp_error = PP_ERROR_FAILED;
539 PP_Resource quota_file_system = 0;
540 if (pp_error == PP_OK) {
541 state_manager_.SetOpenSucceed();
542 // A non-zero resource id signals the plugin side to check quota.
544 quota_file_system = file_system_host_->pp_resource();
547 reply_context.params.set_result(pp_error);
550 PpapiPluginMsg_FileIO_OpenReply(quota_file_system, max_written_offset_));
551 state_manager_.SetOperationFinished();
554 void PepperFileIOHost::SendOpenErrorReply(
555 ppapi::host::ReplyMessageContext reply_context) {
556 host()->SendReply(reply_context, PpapiPluginMsg_FileIO_OpenReply(0, 0));
559 bool PepperFileIOHost::AddFileToReplyContext(
561 ppapi::host::ReplyMessageContext* reply_context) const {
562 IPC::PlatformFileForTransit transit_file =
563 IPC::GetPlatformFileForTransit(file_.GetPlatformFile(), false);
564 if (transit_file == IPC::InvalidPlatformFileForTransit())
567 ppapi::proxy::SerializedHandle file_handle;
568 // A non-zero resource id signals NaClIPCAdapter to create a NaClQuotaDesc.
569 PP_Resource quota_file_io = check_quota_ ? pp_resource() : 0;
570 file_handle.set_file_handle(transit_file, open_flags, quota_file_io);
571 reply_context->params.AppendHandle(std::move(file_handle));
575 } // namespace content