Merge Chromium + Blink git repositories
[chromium-blink-merge.git] / content / browser / renderer_host / pepper / pepper_file_io_host.cc
blob782289118677edd6f617b540144db59d863362e7
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"
7 #include "base/bind.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"
37 namespace content {
39 using ppapi::FileIOStateManager;
40 using ppapi::PPTimeToTime;
42 namespace {
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);
49 if (host) {
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();
55 return stuff;
58 base::ProcessId GetResolvedRenderProcessId(int render_process_id) {
59 DCHECK_CURRENTLY_ON(BrowserThread::UI);
60 RenderProcessHost* host = RenderProcessHost::FromID(render_process_id);
61 if (!host)
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);
71 if (!host)
72 return false;
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,
91 base::File file,
92 const base::Closure& on_close_callback) {
93 if (file_host) {
94 callback.Run(file.Pass(), on_close_callback);
95 } else {
96 BrowserThread::PostTaskAndReply(
97 BrowserThread::FILE,
98 FROM_HERE,
99 base::Bind(&FileCloser, base::Passed(&file)),
100 base::Bind(&DidCloseFile, on_close_callback));
104 } // namespace
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)
113 .get()),
114 open_flags_(0),
115 file_system_type_(PP_FILESYSTEMTYPE_INVALID),
116 max_written_offset_(0),
117 check_quota_(false) {
118 int unused;
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,
134 OnHostMsgSetLength)
135 PPAPI_DISPATCH_HOST_RESOURCE_CALL_0(PpapiHostMsg_FileIO_Flush,
136 OnHostMsgFlush)
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);
156 if (rv != PP_OK)
157 return rv;
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:
197 break;
198 default:
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(
206 BrowserThread::UI,
207 FROM_HERE,
208 base::Bind(&GetUIThreadStuffForInternalFileSystems, render_process_id_),
209 base::Bind(&PepperFileIOHost::GotUIThreadStuffForInternalFileSystems,
210 AsWeakPtr(),
211 context->MakeReplyMessageContext(),
212 platform_file_flags));
213 } else {
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(
218 BrowserThread::UI,
219 FROM_HERE,
220 base::Bind(&GetResolvedRenderProcessId, render_process_id_),
221 base::Bind(&PepperFileIOHost::GotResolvedRenderProcessId,
222 AsWeakPtr(),
223 context->MakeReplyMessageContext(),
224 path,
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);
242 return;
245 if (!file_system_context_->GetFileSystemBackend(file_system_url_.type())) {
246 reply_context.params.set_result(PP_ERROR_FAILED);
247 SendOpenErrorReply(reply_context);
248 return;
251 DCHECK(file_system_host_.get());
252 DCHECK(file_system_host_->GetFileSystemOperationRunner());
254 file_system_host_->GetFileSystemOperationRunner()->OpenFile(
255 file_system_url_,
256 platform_file_flags,
257 base::Bind(&DidOpenFile,
258 AsWeakPtr(),
259 base::Bind(&PepperFileIOHost::DidOpenInternalFile,
260 AsWeakPtr(),
261 reply_context)));
264 void PepperFileIOHost::DidOpenInternalFile(
265 ppapi::host::ReplyMessageContext reply_context,
266 base::File file,
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()) {
272 check_quota_ = true;
273 file_system_host_->OpenQuotaFile(
274 this,
275 file_system_url_,
276 base::Bind(&PepperFileIOHost::DidOpenQuotaFile,
277 AsWeakPtr(),
278 reply_context,
279 base::Passed(&file)));
280 return;
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,
293 base::FilePath path,
294 int file_flags,
295 base::ProcessId resolved_render_process_id) {
296 DCHECK_CURRENTLY_ON(BrowserThread::IO);
297 resolved_render_process_id_ = resolved_render_process_id;
298 file_.CreateOrOpen(
299 path,
300 file_flags,
301 base::Bind(&PepperFileIOHost::OnOpenProxyCallback,
302 AsWeakPtr(),
303 reply_context));
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);
312 if (rv != PP_OK)
313 return rv;
315 if (!file_.SetTimes(
316 PPTimeToTime(last_access_time),
317 PPTimeToTime(last_modified_time),
318 base::Bind(&PepperFileIOHost::ExecutePlatformGeneralCallback,
319 AsWeakPtr(),
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,
330 int64_t length) {
331 int32_t rv = state_manager_.CheckOperationState(
332 FileIOStateManager::OPERATION_EXCLUSIVE, true);
333 if (rv != PP_OK)
334 return rv;
335 if (length < 0)
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(
342 length,
343 base::Bind(&PepperFileIOHost::ExecutePlatformGeneralCallback,
344 AsWeakPtr(),
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);
357 if (rv != PP_OK)
358 return rv;
360 if (!file_.Flush(
361 base::Bind(&PepperFileIOHost::ExecutePlatformGeneralCallback,
362 AsWeakPtr(),
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) {
374 if (check_quota_) {
375 file_system_host_->CloseQuotaFile(this, file_growth);
376 check_quota_ = false;
379 if (file_.IsValid()) {
380 file_.Close(base::Bind(&PepperFileIOHost::DidCloseFile,
381 AsWeakPtr()));
383 return PP_OK;
386 void PepperFileIOHost::DidOpenQuotaFile(
387 ppapi::host::ReplyMessageContext reply_context,
388 base::File file,
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;
411 GURL document_url =
412 browser_ppapi_host_->GetDocumentURLForInstance(pp_instance());
413 BrowserThread::PostTaskAndReplyWithResult(
414 BrowserThread::UI,
415 FROM_HERE,
416 base::Bind(&GetPluginAllowedToCallRequestOSFileHandle,
417 render_process_id_,
418 document_url),
419 base::Bind(&PepperFileIOHost::GotPluginAllowedToCallRequestOSFileHandle,
420 AsWeakPtr(),
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) ||
431 plugin_allowed) {
432 if (!AddFileToReplyContext(open_flags_, &reply_context))
433 reply_context.params.set_result(PP_ERROR_FAILED);
434 } else {
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.
460 if (check_quota_)
461 quota_file_system = file_system_host_->pp_resource();
464 reply_context.params.set_result(pp_error);
465 host()->SendReply(
466 reply_context,
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(
477 int32_t open_flags,
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,
486 false);
487 if (transit_file == IPC::InvalidPlatformFileForTransit())
488 return false;
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);
495 return true;
498 } // namespace content