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 "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" 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 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)); 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 .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; 122 } 123} 124 125PepperFileIOHost::~PepperFileIOHost() {} 126 127int32_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; 142} 143 144PepperFileIOHost::UIThreadStuff::UIThreadStuff() { 145 resolved_render_process_id = base::kNullProcessId; 146} 147 148PepperFileIOHost::UIThreadStuff::~UIThreadStuff() {} 149 150int32_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; 158 159 int platform_file_flags = 0; 160 if (!ppapi::PepperFileOpenFlagsToPlatformFileFlags(open_flags, 161 &platform_file_flags)) 162 return PP_ERROR_BADARGUMENT; 163 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; 172 173 file_system_host_ = file_ref_host->GetFileSystemHost(); 174 175 open_flags_ = open_flags; 176 file_system_type_ = file_ref_host->GetFileSystemType(); 177 file_system_url_ = file_ref_host->GetFileSystemURL(); 178 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(); 183 184 if (is_internal_type) { 185 if (!file_system_url_.is_valid()) 186 return PP_ERROR_BADARGUMENT; 187 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; 200 } 201 } 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)); 226 } 227 state_manager_.SetPendingOperation(FileIOStateManager::OPERATION_EXCLUSIVE); 228 return PP_OK_COMPLETIONPENDING; 229} 230 231void 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; 243 } 244 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; 249 } 250 251 DCHECK(file_system_host_.get()); 252 DCHECK(file_system_host_->GetFileSystemOperationRunner()); 253 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))); 262} 263 264void 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; 270 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; 281 } 282 } 283 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); 289} 290 291void 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)); 304} 305 306int32_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; 314 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; 322 } 323 324 state_manager_.SetPendingOperation(FileIOStateManager::OPERATION_EXCLUSIVE); 325 return PP_OK_COMPLETIONPENDING; 326} 327 328int32_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; 337 338 // Quota checks are performed on the plugin side, in order to use the same 339 // quota reservation and request system as Write. 340 341 if (!file_.SetLength( 342 length, 343 base::Bind(&PepperFileIOHost::ExecutePlatformGeneralCallback, 344 AsWeakPtr(), 345 context->MakeReplyMessageContext()))) { 346 return PP_ERROR_FAILED; 347 } 348 349 state_manager_.SetPendingOperation(FileIOStateManager::OPERATION_EXCLUSIVE); 350 return PP_OK_COMPLETIONPENDING; 351} 352 353int32_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; 359 360 if (!file_.Flush( 361 base::Bind(&PepperFileIOHost::ExecutePlatformGeneralCallback, 362 AsWeakPtr(), 363 context->MakeReplyMessageContext()))) { 364 return PP_ERROR_FAILED; 365 } 366 367 state_manager_.SetPendingOperation(FileIOStateManager::OPERATION_EXCLUSIVE); 368 return PP_OK_COMPLETIONPENDING; 369} 370 371int32_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; 377 } 378 379 if (file_.IsValid()) { 380 file_.Close(base::Bind(&PepperFileIOHost::DidCloseFile, 381 AsWeakPtr())); 382 } 383 return PP_OK; 384} 385 386void 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()); 394 395 OnOpenProxyCallback(reply_context, base::File::FILE_OK); 396} 397 398void 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(); 403 } 404} 405 406int32_t PepperFileIOHost::OnHostMsgRequestOSFileHandle( 407 ppapi::host::HostMessageContext* context) { 408 if (open_flags_ != PP_FILEOPENFLAG_READ && file_system_host_->ChecksQuota()) 409 return PP_ERROR_FAILED; 410 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; 423} 424 425void 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); 436 } 437 host()->SendReply(reply_context, 438 PpapiPluginMsg_FileIO_RequestOSFileHandleReply()); 439} 440 441void 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(); 447} 448 449void 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; 455 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(); 462 } 463 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(); 469} 470 471void PepperFileIOHost::SendOpenErrorReply( 472 ppapi::host::ReplyMessageContext reply_context) { 473 host()->SendReply(reply_context, PpapiPluginMsg_FileIO_OpenReply(0, 0)); 474} 475 476bool 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_->GetPluginProcessHandle()); 481 if (plugin_process_id == base::kNullProcessId) 482 plugin_process_id = resolved_render_process_id_; 483 484 IPC::PlatformFileForTransit transit_file = 485 BrokerGetFileHandleForProcess(file_.GetPlatformFile(), plugin_process_id, 486 false); 487 if (transit_file == IPC::InvalidPlatformFileForTransit()) 488 return false; 489 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; 496} 497 498} // namespace content 499