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. 4 5#include "content/browser/renderer_host/pepper/pepper_file_io_host.h" 6 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" 36 37namespace content { 38 39using ppapi::FileIOStateManager; 40using ppapi::PPTimeToTime; 41 42namespace { 43 44PepperFileIOHost::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(); 54 } 55 return stuff; 56} 57 58base::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()); 64} 65 66bool 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); 75} 76 77bool FileOpenForWrite(int32_t open_flags) { 78 return (open_flags & (PP_FILEOPENFLAG_WRITE | PP_FILEOPENFLAG_APPEND)) != 0; 79} 80 81void FileCloser(base::File auto_close) { 82} 83 84void DidCloseFile(const base::Closure& on_close_callback) { 85 if (!on_close_callback.is_null()) 86 on_close_callback.Run(); 87} 88 89void DidOpenFile(base::WeakPtr<PepperFileIOHost> file_host, 90 fileapi::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)); 101 } 102} 103 104} // namespace 105 106PepperFileIOHost::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 open_flags_(0), 114 file_system_type_(PP_FILESYSTEMTYPE_INVALID), 115 max_written_offset_(0), 116 check_quota_(false) { 117 int unused; 118 if (!host->GetRenderFrameIDsForInstance( 119 instance, &render_process_id_, &unused)) { 120 render_process_id_ = -1; 121 } 122} 123 124PepperFileIOHost::~PepperFileIOHost() {} 125 126int32_t PepperFileIOHost::OnResourceMessageReceived( 127 const IPC::Message& msg, 128 ppapi::host::HostMessageContext* context) { 129 PPAPI_BEGIN_MESSAGE_MAP(PepperFileIOHost, msg) 130 PPAPI_DISPATCH_HOST_RESOURCE_CALL(PpapiHostMsg_FileIO_Open, OnHostMsgOpen) 131 PPAPI_DISPATCH_HOST_RESOURCE_CALL(PpapiHostMsg_FileIO_Touch, OnHostMsgTouch) 132 PPAPI_DISPATCH_HOST_RESOURCE_CALL(PpapiHostMsg_FileIO_SetLength, 133 OnHostMsgSetLength) 134 PPAPI_DISPATCH_HOST_RESOURCE_CALL_0(PpapiHostMsg_FileIO_Flush, 135 OnHostMsgFlush) 136 PPAPI_DISPATCH_HOST_RESOURCE_CALL(PpapiHostMsg_FileIO_Close, OnHostMsgClose) 137 PPAPI_DISPATCH_HOST_RESOURCE_CALL_0(PpapiHostMsg_FileIO_RequestOSFileHandle, 138 OnHostMsgRequestOSFileHandle) 139 PPAPI_END_MESSAGE_MAP() 140 return PP_ERROR_FAILED; 141} 142 143PepperFileIOHost::UIThreadStuff::UIThreadStuff() { 144 resolved_render_process_id = base::kNullProcessId; 145} 146 147PepperFileIOHost::UIThreadStuff::~UIThreadStuff() {} 148 149int32_t PepperFileIOHost::OnHostMsgOpen( 150 ppapi::host::HostMessageContext* context, 151 PP_Resource file_ref_resource, 152 int32_t open_flags) { 153 int32_t rv = state_manager_.CheckOperationState( 154 FileIOStateManager::OPERATION_EXCLUSIVE, false); 155 if (rv != PP_OK) 156 return rv; 157 158 int platform_file_flags = 0; 159 if (!ppapi::PepperFileOpenFlagsToPlatformFileFlags(open_flags, 160 &platform_file_flags)) 161 return PP_ERROR_BADARGUMENT; 162 163 ppapi::host::ResourceHost* resource_host = 164 host()->GetResourceHost(file_ref_resource); 165 if (!resource_host || !resource_host->IsFileRefHost()) 166 return PP_ERROR_BADRESOURCE; 167 PepperFileRefHost* file_ref_host = 168 static_cast<PepperFileRefHost*>(resource_host); 169 if (file_ref_host->GetFileSystemType() == PP_FILESYSTEMTYPE_INVALID) 170 return PP_ERROR_FAILED; 171 172 file_system_host_ = file_ref_host->GetFileSystemHost(); 173 174 open_flags_ = open_flags; 175 file_system_type_ = file_ref_host->GetFileSystemType(); 176 file_system_url_ = file_ref_host->GetFileSystemURL(); 177 178 // For external file systems, if there is a valid FileSystemURL, then treat 179 // it like internal file systems and access it via the FileSystemURL. 180 bool is_internal_type = (file_system_type_ != PP_FILESYSTEMTYPE_EXTERNAL) || 181 file_system_url_.is_valid(); 182 183 if (is_internal_type) { 184 if (!file_system_url_.is_valid()) 185 return PP_ERROR_BADARGUMENT; 186 187 // Not all external file systems are fully supported yet. 188 // Whitelist the supported ones. 189 if (file_system_url_.mount_type() == fileapi::kFileSystemTypeExternal) { 190 switch (file_system_url_.type()) { 191 case fileapi::kFileSystemTypeNativeMedia: 192 case fileapi::kFileSystemTypeDeviceMedia: 193 case fileapi::kFileSystemTypePicasa: 194 case fileapi::kFileSystemTypeItunes: 195 case fileapi::kFileSystemTypeIphoto: 196 break; 197 default: 198 return PP_ERROR_NOACCESS; 199 } 200 } 201 if (!CanOpenFileSystemURLWithPepperFlags( 202 open_flags, render_process_id_, file_system_url_)) 203 return PP_ERROR_NOACCESS; 204 BrowserThread::PostTaskAndReplyWithResult( 205 BrowserThread::UI, 206 FROM_HERE, 207 base::Bind(&GetUIThreadStuffForInternalFileSystems, render_process_id_), 208 base::Bind(&PepperFileIOHost::GotUIThreadStuffForInternalFileSystems, 209 AsWeakPtr(), 210 context->MakeReplyMessageContext(), 211 platform_file_flags)); 212 } else { 213 base::FilePath path = file_ref_host->GetExternalFilePath(); 214 if (!CanOpenWithPepperFlags(open_flags, render_process_id_, path)) 215 return PP_ERROR_NOACCESS; 216 BrowserThread::PostTaskAndReplyWithResult( 217 BrowserThread::UI, 218 FROM_HERE, 219 base::Bind(&GetResolvedRenderProcessId, render_process_id_), 220 base::Bind(&PepperFileIOHost::GotResolvedRenderProcessId, 221 AsWeakPtr(), 222 context->MakeReplyMessageContext(), 223 path, 224 platform_file_flags)); 225 } 226 state_manager_.SetPendingOperation(FileIOStateManager::OPERATION_EXCLUSIVE); 227 return PP_OK_COMPLETIONPENDING; 228} 229 230void PepperFileIOHost::GotUIThreadStuffForInternalFileSystems( 231 ppapi::host::ReplyMessageContext reply_context, 232 int platform_file_flags, 233 UIThreadStuff ui_thread_stuff) { 234 DCHECK_CURRENTLY_ON(BrowserThread::IO); 235 file_system_context_ = ui_thread_stuff.file_system_context; 236 resolved_render_process_id_ = ui_thread_stuff.resolved_render_process_id; 237 if (resolved_render_process_id_ == base::kNullProcessId || 238 !file_system_context_.get()) { 239 reply_context.params.set_result(PP_ERROR_FAILED); 240 SendOpenErrorReply(reply_context); 241 return; 242 } 243 244 if (!file_system_context_->GetFileSystemBackend(file_system_url_.type())) { 245 reply_context.params.set_result(PP_ERROR_FAILED); 246 SendOpenErrorReply(reply_context); 247 return; 248 } 249 250 DCHECK(file_system_host_.get()); 251 DCHECK(file_system_host_->GetFileSystemOperationRunner()); 252 253 file_system_host_->GetFileSystemOperationRunner()->OpenFile( 254 file_system_url_, 255 platform_file_flags, 256 base::Bind(&DidOpenFile, 257 AsWeakPtr(), 258 base::Bind(&PepperFileIOHost::DidOpenInternalFile, 259 AsWeakPtr(), 260 reply_context))); 261} 262 263void PepperFileIOHost::DidOpenInternalFile( 264 ppapi::host::ReplyMessageContext reply_context, 265 base::File file, 266 const base::Closure& on_close_callback) { 267 if (file.IsValid()) { 268 on_close_callback_ = on_close_callback; 269 270 if (FileOpenForWrite(open_flags_) && file_system_host_->ChecksQuota()) { 271 check_quota_ = true; 272 file_system_host_->OpenQuotaFile( 273 this, 274 file_system_url_, 275 base::Bind(&PepperFileIOHost::DidOpenQuotaFile, 276 AsWeakPtr(), 277 reply_context, 278 base::Passed(&file))); 279 return; 280 } 281 } 282 283 DCHECK(!file_.IsValid()); 284 base::File::Error error = 285 file.IsValid() ? base::File::FILE_OK : file.error_details(); 286 file_.SetFile(file.Pass()); 287 OnOpenProxyCallback(reply_context, error); 288} 289 290void PepperFileIOHost::GotResolvedRenderProcessId( 291 ppapi::host::ReplyMessageContext reply_context, 292 base::FilePath path, 293 int file_flags, 294 base::ProcessId resolved_render_process_id) { 295 DCHECK_CURRENTLY_ON(BrowserThread::IO); 296 resolved_render_process_id_ = resolved_render_process_id; 297 file_.CreateOrOpen( 298 path, 299 file_flags, 300 base::Bind(&PepperFileIOHost::OnOpenProxyCallback, 301 AsWeakPtr(), 302 reply_context)); 303} 304 305int32_t PepperFileIOHost::OnHostMsgTouch( 306 ppapi::host::HostMessageContext* context, 307 PP_Time last_access_time, 308 PP_Time last_modified_time) { 309 int32_t rv = state_manager_.CheckOperationState( 310 FileIOStateManager::OPERATION_EXCLUSIVE, true); 311 if (rv != PP_OK) 312 return rv; 313 314 if (!file_.SetTimes( 315 PPTimeToTime(last_access_time), 316 PPTimeToTime(last_modified_time), 317 base::Bind(&PepperFileIOHost::ExecutePlatformGeneralCallback, 318 AsWeakPtr(), 319 context->MakeReplyMessageContext()))) { 320 return PP_ERROR_FAILED; 321 } 322 323 state_manager_.SetPendingOperation(FileIOStateManager::OPERATION_EXCLUSIVE); 324 return PP_OK_COMPLETIONPENDING; 325} 326 327int32_t PepperFileIOHost::OnHostMsgSetLength( 328 ppapi::host::HostMessageContext* context, 329 int64_t length) { 330 int32_t rv = state_manager_.CheckOperationState( 331 FileIOStateManager::OPERATION_EXCLUSIVE, true); 332 if (rv != PP_OK) 333 return rv; 334 if (length < 0) 335 return PP_ERROR_BADARGUMENT; 336 337 // Quota checks are performed on the plugin side, in order to use the same 338 // quota reservation and request system as Write. 339 340 if (!file_.SetLength( 341 length, 342 base::Bind(&PepperFileIOHost::ExecutePlatformGeneralCallback, 343 AsWeakPtr(), 344 context->MakeReplyMessageContext()))) { 345 return PP_ERROR_FAILED; 346 } 347 348 state_manager_.SetPendingOperation(FileIOStateManager::OPERATION_EXCLUSIVE); 349 return PP_OK_COMPLETIONPENDING; 350} 351 352int32_t PepperFileIOHost::OnHostMsgFlush( 353 ppapi::host::HostMessageContext* context) { 354 int32_t rv = state_manager_.CheckOperationState( 355 FileIOStateManager::OPERATION_EXCLUSIVE, true); 356 if (rv != PP_OK) 357 return rv; 358 359 if (!file_.Flush( 360 base::Bind(&PepperFileIOHost::ExecutePlatformGeneralCallback, 361 AsWeakPtr(), 362 context->MakeReplyMessageContext()))) { 363 return PP_ERROR_FAILED; 364 } 365 366 state_manager_.SetPendingOperation(FileIOStateManager::OPERATION_EXCLUSIVE); 367 return PP_OK_COMPLETIONPENDING; 368} 369 370int32_t PepperFileIOHost::OnHostMsgClose( 371 ppapi::host::HostMessageContext* context, 372 const ppapi::FileGrowth& file_growth) { 373 if (check_quota_) { 374 file_system_host_->CloseQuotaFile(this, file_growth); 375 check_quota_ = false; 376 } 377 378 if (file_.IsValid()) { 379 file_.Close(base::Bind(&PepperFileIOHost::DidCloseFile, 380 AsWeakPtr())); 381 } 382 return PP_OK; 383} 384 385void PepperFileIOHost::DidOpenQuotaFile( 386 ppapi::host::ReplyMessageContext reply_context, 387 base::File file, 388 int64_t max_written_offset) { 389 DCHECK(!file_.IsValid()); 390 DCHECK(file.IsValid()); 391 max_written_offset_ = max_written_offset; 392 file_.SetFile(file.Pass()); 393 394 OnOpenProxyCallback(reply_context, base::File::FILE_OK); 395} 396 397void PepperFileIOHost::DidCloseFile(base::File::Error /*error*/) { 398 // Silently ignore if we fail to close the file. 399 if (!on_close_callback_.is_null()) { 400 on_close_callback_.Run(); 401 on_close_callback_.Reset(); 402 } 403} 404 405int32_t PepperFileIOHost::OnHostMsgRequestOSFileHandle( 406 ppapi::host::HostMessageContext* context) { 407 if (open_flags_ != PP_FILEOPENFLAG_READ && file_system_host_->ChecksQuota()) 408 return PP_ERROR_FAILED; 409 410 GURL document_url = 411 browser_ppapi_host_->GetDocumentURLForInstance(pp_instance()); 412 BrowserThread::PostTaskAndReplyWithResult( 413 BrowserThread::UI, 414 FROM_HERE, 415 base::Bind(&GetPluginAllowedToCallRequestOSFileHandle, 416 render_process_id_, 417 document_url), 418 base::Bind(&PepperFileIOHost::GotPluginAllowedToCallRequestOSFileHandle, 419 AsWeakPtr(), 420 context->MakeReplyMessageContext())); 421 return PP_OK_COMPLETIONPENDING; 422} 423 424void PepperFileIOHost::GotPluginAllowedToCallRequestOSFileHandle( 425 ppapi::host::ReplyMessageContext reply_context, 426 bool plugin_allowed) { 427 DCHECK_CURRENTLY_ON(BrowserThread::IO); 428 if (!browser_ppapi_host_->external_plugin() || 429 host()->permissions().HasPermission(ppapi::PERMISSION_PRIVATE) || 430 plugin_allowed) { 431 if (!AddFileToReplyContext(open_flags_, &reply_context)) 432 reply_context.params.set_result(PP_ERROR_FAILED); 433 } else { 434 reply_context.params.set_result(PP_ERROR_NOACCESS); 435 } 436 host()->SendReply(reply_context, 437 PpapiPluginMsg_FileIO_RequestOSFileHandleReply()); 438} 439 440void PepperFileIOHost::ExecutePlatformGeneralCallback( 441 ppapi::host::ReplyMessageContext reply_context, 442 base::File::Error error_code) { 443 reply_context.params.set_result(ppapi::FileErrorToPepperError(error_code)); 444 host()->SendReply(reply_context, PpapiPluginMsg_FileIO_GeneralReply()); 445 state_manager_.SetOperationFinished(); 446} 447 448void PepperFileIOHost::OnOpenProxyCallback( 449 ppapi::host::ReplyMessageContext reply_context, 450 base::File::Error error_code) { 451 int32_t pp_error = ppapi::FileErrorToPepperError(error_code); 452 if (file_.IsValid() && !AddFileToReplyContext(open_flags_, &reply_context)) 453 pp_error = PP_ERROR_FAILED; 454 455 PP_Resource quota_file_system = 0; 456 if (pp_error == PP_OK) { 457 state_manager_.SetOpenSucceed(); 458 // A non-zero resource id signals the plugin side to check quota. 459 if (check_quota_) 460 quota_file_system = file_system_host_->pp_resource(); 461 } 462 463 reply_context.params.set_result(pp_error); 464 host()->SendReply( 465 reply_context, 466 PpapiPluginMsg_FileIO_OpenReply(quota_file_system, max_written_offset_)); 467 state_manager_.SetOperationFinished(); 468} 469 470void PepperFileIOHost::SendOpenErrorReply( 471 ppapi::host::ReplyMessageContext reply_context) { 472 host()->SendReply(reply_context, PpapiPluginMsg_FileIO_OpenReply(0, 0)); 473} 474 475bool PepperFileIOHost::AddFileToReplyContext( 476 int32_t open_flags, 477 ppapi::host::ReplyMessageContext* reply_context) const { 478 base::ProcessId plugin_process_id = 479 base::GetProcId(browser_ppapi_host_->GetPluginProcessHandle()); 480 if (plugin_process_id == base::kNullProcessId) 481 plugin_process_id = resolved_render_process_id_; 482 483 IPC::PlatformFileForTransit transit_file = 484 BrokerGetFileHandleForProcess(file_.GetPlatformFile(), plugin_process_id, 485 false); 486 if (transit_file == IPC::InvalidPlatformFileForTransit()) 487 return false; 488 489 ppapi::proxy::SerializedHandle file_handle; 490 // A non-zero resource id signals NaClIPCAdapter to create a NaClQuotaDesc. 491 PP_Resource quota_file_io = check_quota_ ? pp_resource() : 0; 492 file_handle.set_file_handle(transit_file, open_flags, quota_file_io); 493 reply_context->params.AppendHandle(file_handle); 494 return true; 495} 496 497} // namespace content 498