Revert 268405 "Make sure that ScratchBuffer::Allocate() always r..."
[chromium-blink-merge.git] / content / browser / renderer_host / pepper / pepper_file_io_host.cc
blob56b6d922ac3103e71e49e69d62aedbad12aab2a6
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 "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"
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 } // namespace
83 PepperFileIOHost::PepperFileIOHost(BrowserPpapiHostImpl* host,
84 PP_Instance instance,
85 PP_Resource resource)
86 : ResourceHost(host->GetPpapiHost(), instance, resource),
87 browser_ppapi_host_(host),
88 render_process_host_(NULL),
89 file_(base::kInvalidPlatformFileValue),
90 open_flags_(0),
91 file_system_type_(PP_FILESYSTEMTYPE_INVALID),
92 max_written_offset_(0),
93 check_quota_(false),
94 weak_factory_(this) {
95 int unused;
96 if (!host->GetRenderFrameIDsForInstance(
97 instance, &render_process_id_, &unused)) {
98 render_process_id_ = -1;
100 file_message_loop_ =
101 BrowserThread::GetMessageLoopProxyForThread(BrowserThread::FILE);
104 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, OnHostMsgOpen)
111 PPAPI_DISPATCH_HOST_RESOURCE_CALL(PpapiHostMsg_FileIO_Touch, OnHostMsgTouch)
112 PPAPI_DISPATCH_HOST_RESOURCE_CALL(PpapiHostMsg_FileIO_SetLength,
113 OnHostMsgSetLength)
114 PPAPI_DISPATCH_HOST_RESOURCE_CALL_0(PpapiHostMsg_FileIO_Flush, OnHostMsgFlush)
115 PPAPI_DISPATCH_HOST_RESOURCE_CALL(PpapiHostMsg_FileIO_Close, OnHostMsgClose)
116 PPAPI_DISPATCH_HOST_RESOURCE_CALL_0(PpapiHostMsg_FileIO_RequestOSFileHandle,
117 OnHostMsgRequestOSFileHandle)
118 IPC_END_MESSAGE_MAP()
119 return PP_ERROR_FAILED;
122 PepperFileIOHost::UIThreadStuff::UIThreadStuff() {
123 resolved_render_process_id = base::kNullProcessId;
126 PepperFileIOHost::UIThreadStuff::~UIThreadStuff() {}
128 int32_t PepperFileIOHost::OnHostMsgOpen(
129 ppapi::host::HostMessageContext* context,
130 PP_Resource file_ref_resource,
131 int32_t open_flags) {
132 int32_t rv = state_manager_.CheckOperationState(
133 FileIOStateManager::OPERATION_EXCLUSIVE, false);
134 if (rv != PP_OK)
135 return rv;
137 int platform_file_flags = 0;
138 if (!ppapi::PepperFileOpenFlagsToPlatformFileFlags(open_flags,
139 &platform_file_flags))
140 return PP_ERROR_BADARGUMENT;
142 ppapi::host::ResourceHost* resource_host =
143 host()->GetResourceHost(file_ref_resource);
144 if (!resource_host || !resource_host->IsFileRefHost())
145 return PP_ERROR_BADRESOURCE;
146 PepperFileRefHost* file_ref_host =
147 static_cast<PepperFileRefHost*>(resource_host);
148 if (file_ref_host->GetFileSystemType() == PP_FILESYSTEMTYPE_INVALID)
149 return PP_ERROR_FAILED;
151 file_system_host_ = file_ref_host->GetFileSystemHost();
153 open_flags_ = open_flags;
154 file_system_type_ = file_ref_host->GetFileSystemType();
155 file_system_url_ = file_ref_host->GetFileSystemURL();
157 // For external file systems, if there is a valid FileSystemURL, then treat
158 // it like internal file systems and access it via the FileSystemURL.
159 bool is_internal_type = (file_system_type_ != PP_FILESYSTEMTYPE_EXTERNAL) ||
160 file_system_url_.is_valid();
162 if (is_internal_type) {
163 if (!file_system_url_.is_valid())
164 return PP_ERROR_BADARGUMENT;
166 // Not all external file systems are fully supported yet.
167 // Whitelist the supported ones.
168 if (file_system_url_.mount_type() == fileapi::kFileSystemTypeExternal) {
169 switch (file_system_url_.type()) {
170 case fileapi::kFileSystemTypeNativeMedia:
171 case fileapi::kFileSystemTypeDeviceMedia:
172 case fileapi::kFileSystemTypePicasa:
173 case fileapi::kFileSystemTypeItunes:
174 case fileapi::kFileSystemTypeIphoto:
175 break;
176 default:
177 return PP_ERROR_NOACCESS;
180 if (!CanOpenFileSystemURLWithPepperFlags(
181 open_flags, render_process_id_, file_system_url_))
182 return PP_ERROR_NOACCESS;
183 BrowserThread::PostTaskAndReplyWithResult(
184 BrowserThread::UI,
185 FROM_HERE,
186 base::Bind(&GetUIThreadStuffForInternalFileSystems, render_process_id_),
187 base::Bind(&PepperFileIOHost::GotUIThreadStuffForInternalFileSystems,
188 weak_factory_.GetWeakPtr(),
189 context->MakeReplyMessageContext(),
190 platform_file_flags));
191 } else {
192 base::FilePath path = file_ref_host->GetExternalFilePath();
193 if (!CanOpenWithPepperFlags(open_flags, render_process_id_, path))
194 return PP_ERROR_NOACCESS;
195 BrowserThread::PostTaskAndReplyWithResult(
196 BrowserThread::UI,
197 FROM_HERE,
198 base::Bind(&GetResolvedRenderProcessId, render_process_id_),
199 base::Bind(&PepperFileIOHost::GotResolvedRenderProcessId,
200 weak_factory_.GetWeakPtr(),
201 context->MakeReplyMessageContext(),
202 path,
203 platform_file_flags));
205 state_manager_.SetPendingOperation(FileIOStateManager::OPERATION_EXCLUSIVE);
206 return PP_OK_COMPLETIONPENDING;
209 void PepperFileIOHost::GotUIThreadStuffForInternalFileSystems(
210 ppapi::host::ReplyMessageContext reply_context,
211 int platform_file_flags,
212 UIThreadStuff ui_thread_stuff) {
213 DCHECK_CURRENTLY_ON(BrowserThread::IO);
214 file_system_context_ = ui_thread_stuff.file_system_context;
215 resolved_render_process_id_ = ui_thread_stuff.resolved_render_process_id;
216 if (resolved_render_process_id_ == base::kNullProcessId ||
217 !file_system_context_.get()) {
218 reply_context.params.set_result(PP_ERROR_FAILED);
219 SendOpenErrorReply(reply_context);
220 return;
223 if (!file_system_context_->GetFileSystemBackend(file_system_url_.type())) {
224 reply_context.params.set_result(PP_ERROR_FAILED);
225 SendOpenErrorReply(reply_context);
226 return;
229 DCHECK(file_system_host_.get());
230 DCHECK(file_system_host_->GetFileSystemOperationRunner());
231 file_system_host_->GetFileSystemOperationRunner()->OpenFile(
232 file_system_url_,
233 platform_file_flags,
234 base::Bind(&PepperFileIOHost::DidOpenInternalFile,
235 weak_factory_.GetWeakPtr(),
236 reply_context));
239 void PepperFileIOHost::DidOpenInternalFile(
240 ppapi::host::ReplyMessageContext reply_context,
241 base::File::Error result,
242 base::PlatformFile file,
243 const base::Closure& on_close_callback) {
244 if (result == base::File::FILE_OK) {
245 on_close_callback_ = on_close_callback;
247 if (FileOpenForWrite(open_flags_) && file_system_host_->ChecksQuota()) {
248 check_quota_ = true;
249 file_system_host_->OpenQuotaFile(
250 this,
251 file_system_url_,
252 base::Bind(&PepperFileIOHost::DidOpenQuotaFile,
253 weak_factory_.GetWeakPtr(),
254 reply_context,
255 file));
256 return;
260 ExecutePlatformOpenFileCallback(
261 reply_context, result, base::PassPlatformFile(&file), true);
264 void PepperFileIOHost::GotResolvedRenderProcessId(
265 ppapi::host::ReplyMessageContext reply_context,
266 base::FilePath path,
267 int platform_file_flags,
268 base::ProcessId resolved_render_process_id) {
269 DCHECK_CURRENTLY_ON(BrowserThread::IO);
270 resolved_render_process_id_ = resolved_render_process_id;
271 base::FileUtilProxy::CreateOrOpen(
272 file_message_loop_,
273 path,
274 platform_file_flags,
275 base::Bind(&PepperFileIOHost::ExecutePlatformOpenFileCallback,
276 weak_factory_.GetWeakPtr(),
277 reply_context));
280 int32_t PepperFileIOHost::OnHostMsgTouch(
281 ppapi::host::HostMessageContext* context,
282 PP_Time last_access_time,
283 PP_Time last_modified_time) {
284 int32_t rv = state_manager_.CheckOperationState(
285 FileIOStateManager::OPERATION_EXCLUSIVE, true);
286 if (rv != PP_OK)
287 return rv;
289 if (!base::FileUtilProxy::Touch(
290 file_message_loop_,
291 file_,
292 PPTimeToTime(last_access_time),
293 PPTimeToTime(last_modified_time),
294 base::Bind(&PepperFileIOHost::ExecutePlatformGeneralCallback,
295 weak_factory_.GetWeakPtr(),
296 context->MakeReplyMessageContext())))
297 return PP_ERROR_FAILED;
299 state_manager_.SetPendingOperation(FileIOStateManager::OPERATION_EXCLUSIVE);
300 return PP_OK_COMPLETIONPENDING;
303 int32_t PepperFileIOHost::OnHostMsgSetLength(
304 ppapi::host::HostMessageContext* context,
305 int64_t length) {
306 int32_t rv = state_manager_.CheckOperationState(
307 FileIOStateManager::OPERATION_EXCLUSIVE, true);
308 if (rv != PP_OK)
309 return rv;
310 if (length < 0)
311 return PP_ERROR_BADARGUMENT;
313 // Quota checks are performed on the plugin side, in order to use the same
314 // quota reservation and request system as Write.
316 if (!base::FileUtilProxy::Truncate(
317 file_message_loop_,
318 file_,
319 length,
320 base::Bind(&PepperFileIOHost::ExecutePlatformGeneralCallback,
321 weak_factory_.GetWeakPtr(),
322 context->MakeReplyMessageContext())))
323 return PP_ERROR_FAILED;
325 state_manager_.SetPendingOperation(FileIOStateManager::OPERATION_EXCLUSIVE);
326 return PP_OK_COMPLETIONPENDING;
329 int32_t PepperFileIOHost::OnHostMsgFlush(
330 ppapi::host::HostMessageContext* context) {
331 int32_t rv = state_manager_.CheckOperationState(
332 FileIOStateManager::OPERATION_EXCLUSIVE, true);
333 if (rv != PP_OK)
334 return rv;
336 if (!base::FileUtilProxy::Flush(
337 file_message_loop_,
338 file_,
339 base::Bind(&PepperFileIOHost::ExecutePlatformGeneralCallback,
340 weak_factory_.GetWeakPtr(),
341 context->MakeReplyMessageContext())))
342 return PP_ERROR_FAILED;
344 state_manager_.SetPendingOperation(FileIOStateManager::OPERATION_EXCLUSIVE);
345 return PP_OK_COMPLETIONPENDING;
348 int32_t PepperFileIOHost::OnHostMsgClose(
349 ppapi::host::HostMessageContext* context,
350 const ppapi::FileGrowth& file_growth) {
351 if (check_quota_) {
352 file_system_host_->CloseQuotaFile(this, file_growth);
353 check_quota_ = false;
356 if (file_ != base::kInvalidPlatformFileValue) {
357 base::FileUtilProxy::Close(file_message_loop_,
358 file_,
359 base::Bind(&PepperFileIOHost::DidCloseFile,
360 weak_factory_.GetWeakPtr()));
361 file_ = base::kInvalidPlatformFileValue;
363 return PP_OK;
366 void PepperFileIOHost::DidOpenQuotaFile(
367 ppapi::host::ReplyMessageContext reply_context,
368 base::PlatformFile file,
369 int64_t max_written_offset) {
370 max_written_offset_ = max_written_offset;
372 ExecutePlatformOpenFileCallback(
373 reply_context, base::File::FILE_OK, base::PassPlatformFile(&file), true);
376 void PepperFileIOHost::DidCloseFile(base::File::Error /*error*/) {
377 // Silently ignore if we fail to close the file.
378 if (!on_close_callback_.is_null()) {
379 on_close_callback_.Run();
380 on_close_callback_.Reset();
384 int32_t PepperFileIOHost::OnHostMsgRequestOSFileHandle(
385 ppapi::host::HostMessageContext* context) {
386 if (open_flags_ != PP_FILEOPENFLAG_READ && file_system_host_->ChecksQuota())
387 return PP_ERROR_FAILED;
389 GURL document_url =
390 browser_ppapi_host_->GetDocumentURLForInstance(pp_instance());
391 BrowserThread::PostTaskAndReplyWithResult(
392 BrowserThread::UI,
393 FROM_HERE,
394 base::Bind(&GetPluginAllowedToCallRequestOSFileHandle,
395 render_process_id_,
396 document_url),
397 base::Bind(&PepperFileIOHost::GotPluginAllowedToCallRequestOSFileHandle,
398 weak_factory_.GetWeakPtr(),
399 context->MakeReplyMessageContext()));
400 return PP_OK_COMPLETIONPENDING;
403 void PepperFileIOHost::GotPluginAllowedToCallRequestOSFileHandle(
404 ppapi::host::ReplyMessageContext reply_context,
405 bool plugin_allowed) {
406 DCHECK_CURRENTLY_ON(BrowserThread::IO);
407 if (!browser_ppapi_host_->external_plugin() ||
408 host()->permissions().HasPermission(ppapi::PERMISSION_PRIVATE) ||
409 plugin_allowed) {
410 if (!AddFileToReplyContext(open_flags_, &reply_context))
411 reply_context.params.set_result(PP_ERROR_FAILED);
412 } else {
413 reply_context.params.set_result(PP_ERROR_NOACCESS);
415 host()->SendReply(reply_context,
416 PpapiPluginMsg_FileIO_RequestOSFileHandleReply());
419 void PepperFileIOHost::ExecutePlatformGeneralCallback(
420 ppapi::host::ReplyMessageContext reply_context,
421 base::File::Error error_code) {
422 reply_context.params.set_result(ppapi::FileErrorToPepperError(error_code));
423 host()->SendReply(reply_context, PpapiPluginMsg_FileIO_GeneralReply());
424 state_manager_.SetOperationFinished();
427 void PepperFileIOHost::ExecutePlatformOpenFileCallback(
428 ppapi::host::ReplyMessageContext reply_context,
429 base::File::Error error_code,
430 base::PassPlatformFile file,
431 bool unused_created) {
432 int32_t pp_error = ppapi::FileErrorToPepperError(error_code);
433 DCHECK(file_ == base::kInvalidPlatformFileValue);
434 file_ = file.ReleaseValue();
436 if (file_ != base::kInvalidPlatformFileValue &&
437 !AddFileToReplyContext(open_flags_, &reply_context))
438 pp_error = PP_ERROR_FAILED;
440 PP_Resource quota_file_system = 0;
441 if (pp_error == PP_OK) {
442 state_manager_.SetOpenSucceed();
443 // A non-zero resource id signals the plugin side to check quota.
444 if (check_quota_)
445 quota_file_system = file_system_host_->pp_resource();
448 reply_context.params.set_result(pp_error);
449 host()->SendReply(
450 reply_context,
451 PpapiPluginMsg_FileIO_OpenReply(quota_file_system, max_written_offset_));
452 state_manager_.SetOperationFinished();
455 void PepperFileIOHost::SendOpenErrorReply(
456 ppapi::host::ReplyMessageContext reply_context) {
457 host()->SendReply(reply_context, PpapiPluginMsg_FileIO_OpenReply(0, 0));
460 bool PepperFileIOHost::AddFileToReplyContext(
461 int32_t open_flags,
462 ppapi::host::ReplyMessageContext* reply_context) const {
463 base::ProcessId plugin_process_id =
464 base::GetProcId(browser_ppapi_host_->GetPluginProcessHandle());
465 if (plugin_process_id == base::kNullProcessId)
466 plugin_process_id = resolved_render_process_id_;
468 IPC::PlatformFileForTransit transit_file =
469 BrokerGetFileHandleForProcess(file_, plugin_process_id, false);
470 if (transit_file == IPC::InvalidPlatformFileForTransit())
471 return false;
473 ppapi::proxy::SerializedHandle file_handle;
474 // A non-zero resource id signals NaClIPCAdapter to create a NaClQuotaDesc.
475 PP_Resource quota_file_io = check_quota_ ? pp_resource() : 0;
476 file_handle.set_file_handle(transit_file, open_flags, quota_file_io);
477 reply_context->params.AppendHandle(file_handle);
478 return true;
481 } // namespace content