file_io_resource.cc revision 0f1bc08d4cfcc34181b0b5cbf065c40f687bf740
1// Copyright (c) 2012 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 "ppapi/proxy/file_io_resource.h" 6 7#include "base/bind.h" 8#include "base/task_runner_util.h" 9#include "ipc/ipc_message.h" 10#include "ppapi/c/pp_errors.h" 11#include "ppapi/proxy/ppapi_messages.h" 12#include "ppapi/shared_impl/array_writer.h" 13#include "ppapi/shared_impl/file_type_conversion.h" 14#include "ppapi/shared_impl/ppapi_globals.h" 15#include "ppapi/shared_impl/proxy_lock.h" 16#include "ppapi/shared_impl/resource_tracker.h" 17#include "ppapi/thunk/enter.h" 18#include "ppapi/thunk/ppb_file_ref_api.h" 19 20using ppapi::thunk::EnterResourceNoLock; 21using ppapi::thunk::PPB_FileIO_API; 22using ppapi::thunk::PPB_FileRef_API; 23 24namespace { 25 26// We must allocate a buffer sized according to the request of the plugin. To 27// reduce the chance of out-of-memory errors, we cap the read size to 32MB. 28// This is OK since the API specifies that it may perform a partial read. 29static const int32_t kMaxReadSize = 32 * 1024 * 1024; // 32MB 30 31// An adapter to let Read() share the same implementation with ReadToArray(). 32void* DummyGetDataBuffer(void* user_data, uint32_t count, uint32_t size) { 33 return user_data; 34} 35 36// File thread task to close the file handle. 37void DoClose(base::PlatformFile file) { 38 base::ClosePlatformFile(file); 39} 40 41} // namespace 42 43namespace ppapi { 44namespace proxy { 45 46FileIOResource::QueryOp::QueryOp(scoped_refptr<FileHandleHolder> file_handle) 47 : file_handle_(file_handle) { 48 DCHECK(file_handle_); 49} 50 51FileIOResource::QueryOp::~QueryOp() { 52} 53 54int32_t FileIOResource::QueryOp::DoWork() { 55 return base::GetPlatformFileInfo(file_handle_->raw_handle(), &file_info_) ? 56 PP_OK : PP_ERROR_FAILED; 57} 58 59FileIOResource::ReadOp::ReadOp(scoped_refptr<FileHandleHolder> file_handle, 60 int64_t offset, 61 int32_t bytes_to_read) 62 : file_handle_(file_handle), 63 offset_(offset), 64 bytes_to_read_(bytes_to_read) { 65 DCHECK(file_handle_); 66} 67 68FileIOResource::ReadOp::~ReadOp() { 69} 70 71int32_t FileIOResource::ReadOp::DoWork() { 72 DCHECK(!buffer_.get()); 73 buffer_.reset(new char[bytes_to_read_]); 74 return base::ReadPlatformFile( 75 file_handle_->raw_handle(), offset_, buffer_.get(), bytes_to_read_); 76} 77 78FileIOResource::FileIOResource(Connection connection, PP_Instance instance) 79 : PluginResource(connection, instance), 80 file_system_type_(PP_FILESYSTEMTYPE_INVALID) { 81 SendCreate(BROWSER, PpapiHostMsg_FileIO_Create()); 82} 83 84FileIOResource::~FileIOResource() { 85} 86 87PPB_FileIO_API* FileIOResource::AsPPB_FileIO_API() { 88 return this; 89} 90 91int32_t FileIOResource::Open(PP_Resource file_ref, 92 int32_t open_flags, 93 scoped_refptr<TrackedCallback> callback) { 94 EnterResourceNoLock<PPB_FileRef_API> enter(file_ref, true); 95 if (enter.failed()) 96 return PP_ERROR_BADRESOURCE; 97 98 PPB_FileRef_API* file_ref_api = enter.object(); 99 PP_FileSystemType type = file_ref_api->GetFileSystemType(); 100 if (type != PP_FILESYSTEMTYPE_LOCALPERSISTENT && 101 type != PP_FILESYSTEMTYPE_LOCALTEMPORARY && 102 type != PP_FILESYSTEMTYPE_EXTERNAL && 103 type != PP_FILESYSTEMTYPE_ISOLATED) { 104 NOTREACHED(); 105 return PP_ERROR_FAILED; 106 } 107 file_system_type_ = type; 108 109 int32_t rv = state_manager_.CheckOperationState( 110 FileIOStateManager::OPERATION_EXCLUSIVE, false); 111 if (rv != PP_OK) 112 return rv; 113 114 // Take a reference on the FileRef resource while we're opening the file; we 115 // don't want the plugin destroying it during the Open operation. 116 file_ref_ = enter.resource(); 117 118 Call<PpapiPluginMsg_FileIO_OpenReply>(BROWSER, 119 PpapiHostMsg_FileIO_Open( 120 file_ref, 121 open_flags), 122 base::Bind(&FileIOResource::OnPluginMsgOpenFileComplete, this, 123 callback)); 124 125 state_manager_.SetPendingOperation(FileIOStateManager::OPERATION_EXCLUSIVE); 126 return PP_OK_COMPLETIONPENDING; 127} 128 129int32_t FileIOResource::Query(PP_FileInfo* info, 130 scoped_refptr<TrackedCallback> callback) { 131 int32_t rv = state_manager_.CheckOperationState( 132 FileIOStateManager::OPERATION_EXCLUSIVE, true); 133 if (rv != PP_OK) 134 return rv; 135 if (!info) 136 return PP_ERROR_BADARGUMENT; 137 if (!FileHandleHolder::IsValid(file_handle_)) 138 return PP_ERROR_FAILED; 139 140 state_manager_.SetPendingOperation(FileIOStateManager::OPERATION_EXCLUSIVE); 141 142 // If the callback is blocking, perform the task on the calling thread. 143 if (callback->is_blocking()) { 144 int32_t result = PP_ERROR_FAILED; 145 base::PlatformFileInfo file_info; 146 // The plugin could release its reference to this instance when we release 147 // the proxy lock below. 148 scoped_refptr<FileIOResource> protect(this); 149 { 150 // Release the proxy lock while making a potentially slow file call. 151 ProxyAutoUnlock unlock; 152 if (base::GetPlatformFileInfo(file_handle_->raw_handle(), &file_info)) 153 result = PP_OK; 154 } 155 if (result == PP_OK) { 156 // This writes the file info into the plugin's PP_FileInfo struct. 157 ppapi::PlatformFileInfoToPepperFileInfo(file_info, 158 file_system_type_, 159 info); 160 } 161 state_manager_.SetOperationFinished(); 162 return result; 163 } 164 165 // For the non-blocking case, post a task to the file thread and add a 166 // completion task to write the result. 167 scoped_refptr<QueryOp> query_op(new QueryOp(file_handle_)); 168 base::PostTaskAndReplyWithResult( 169 PpapiGlobals::Get()->GetFileTaskRunner(), 170 FROM_HERE, 171 Bind(&FileIOResource::QueryOp::DoWork, query_op), 172 RunWhileLocked(Bind(&TrackedCallback::Run, callback))); 173 callback->set_completion_task( 174 Bind(&FileIOResource::OnQueryComplete, this, query_op, info)); 175 176 return PP_OK_COMPLETIONPENDING; 177} 178 179int32_t FileIOResource::Touch(PP_Time last_access_time, 180 PP_Time last_modified_time, 181 scoped_refptr<TrackedCallback> callback) { 182 int32_t rv = state_manager_.CheckOperationState( 183 FileIOStateManager::OPERATION_EXCLUSIVE, true); 184 if (rv != PP_OK) 185 return rv; 186 187 Call<PpapiPluginMsg_FileIO_GeneralReply>(BROWSER, 188 PpapiHostMsg_FileIO_Touch(last_access_time, last_modified_time), 189 base::Bind(&FileIOResource::OnPluginMsgGeneralComplete, this, 190 callback)); 191 192 state_manager_.SetPendingOperation(FileIOStateManager::OPERATION_EXCLUSIVE); 193 return PP_OK_COMPLETIONPENDING; 194} 195 196int32_t FileIOResource::Read(int64_t offset, 197 char* buffer, 198 int32_t bytes_to_read, 199 scoped_refptr<TrackedCallback> callback) { 200 int32_t rv = state_manager_.CheckOperationState( 201 FileIOStateManager::OPERATION_READ, true); 202 if (rv != PP_OK) 203 return rv; 204 205 PP_ArrayOutput output_adapter; 206 output_adapter.GetDataBuffer = &DummyGetDataBuffer; 207 output_adapter.user_data = buffer; 208 return ReadValidated(offset, bytes_to_read, output_adapter, callback); 209} 210 211int32_t FileIOResource::ReadToArray(int64_t offset, 212 int32_t max_read_length, 213 PP_ArrayOutput* array_output, 214 scoped_refptr<TrackedCallback> callback) { 215 DCHECK(array_output); 216 int32_t rv = state_manager_.CheckOperationState( 217 FileIOStateManager::OPERATION_READ, true); 218 if (rv != PP_OK) 219 return rv; 220 221 return ReadValidated(offset, max_read_length, *array_output, callback); 222} 223 224int32_t FileIOResource::Write(int64_t offset, 225 const char* buffer, 226 int32_t bytes_to_write, 227 scoped_refptr<TrackedCallback> callback) { 228 int32_t rv = state_manager_.CheckOperationState( 229 FileIOStateManager::OPERATION_WRITE, true); 230 if (rv != PP_OK) 231 return rv; 232 233 // TODO(brettw) it would be nice to use a shared memory buffer for large 234 // writes rather than having to copy to a string (which will involve a number 235 // of extra copies to serialize over IPC). 236 Call<PpapiPluginMsg_FileIO_GeneralReply>(BROWSER, 237 PpapiHostMsg_FileIO_Write(offset, std::string(buffer, bytes_to_write)), 238 base::Bind(&FileIOResource::OnPluginMsgGeneralComplete, this, 239 callback)); 240 241 state_manager_.SetPendingOperation(FileIOStateManager::OPERATION_WRITE); 242 return PP_OK_COMPLETIONPENDING; 243} 244 245int32_t FileIOResource::SetLength(int64_t length, 246 scoped_refptr<TrackedCallback> callback) { 247 int32_t rv = state_manager_.CheckOperationState( 248 FileIOStateManager::OPERATION_EXCLUSIVE, true); 249 if (rv != PP_OK) 250 return rv; 251 252 Call<PpapiPluginMsg_FileIO_GeneralReply>(BROWSER, 253 PpapiHostMsg_FileIO_SetLength(length), 254 base::Bind(&FileIOResource::OnPluginMsgGeneralComplete, this, 255 callback)); 256 257 state_manager_.SetPendingOperation(FileIOStateManager::OPERATION_EXCLUSIVE); 258 return PP_OK_COMPLETIONPENDING; 259} 260 261int32_t FileIOResource::Flush(scoped_refptr<TrackedCallback> callback) { 262 int32_t rv = state_manager_.CheckOperationState( 263 FileIOStateManager::OPERATION_EXCLUSIVE, true); 264 if (rv != PP_OK) 265 return rv; 266 267 Call<PpapiPluginMsg_FileIO_GeneralReply>(BROWSER, 268 PpapiHostMsg_FileIO_Flush(), 269 base::Bind(&FileIOResource::OnPluginMsgGeneralComplete, this, 270 callback)); 271 272 state_manager_.SetPendingOperation(FileIOStateManager::OPERATION_EXCLUSIVE); 273 return PP_OK_COMPLETIONPENDING; 274} 275 276void FileIOResource::Close() { 277 if (file_handle_) { 278 file_handle_ = NULL; 279 } 280 Post(BROWSER, PpapiHostMsg_FileIO_Close()); 281} 282 283int32_t FileIOResource::RequestOSFileHandle( 284 PP_FileHandle* handle, 285 scoped_refptr<TrackedCallback> callback) { 286 int32_t rv = state_manager_.CheckOperationState( 287 FileIOStateManager::OPERATION_EXCLUSIVE, true); 288 if (rv != PP_OK) 289 return rv; 290 291 Call<PpapiPluginMsg_FileIO_RequestOSFileHandleReply>(BROWSER, 292 PpapiHostMsg_FileIO_RequestOSFileHandle(), 293 base::Bind(&FileIOResource::OnPluginMsgRequestOSFileHandleComplete, this, 294 callback, handle)); 295 296 state_manager_.SetPendingOperation(FileIOStateManager::OPERATION_EXCLUSIVE); 297 return PP_OK_COMPLETIONPENDING; 298} 299 300FileIOResource::FileHandleHolder::FileHandleHolder(PP_FileHandle file_handle) 301 : raw_handle_(file_handle) { 302} 303 304// static 305bool FileIOResource::FileHandleHolder::IsValid( 306 const scoped_refptr<FileIOResource::FileHandleHolder>& handle) { 307 return handle && (handle->raw_handle() != base::kInvalidPlatformFileValue); 308} 309 310FileIOResource::FileHandleHolder::~FileHandleHolder() { 311 if (raw_handle_ != base::kInvalidPlatformFileValue) { 312 base::TaskRunner* file_task_runner = 313 PpapiGlobals::Get()->GetFileTaskRunner(); 314 file_task_runner->PostTask(FROM_HERE, 315 base::Bind(&DoClose, raw_handle_)); 316 } 317} 318 319int32_t FileIOResource::ReadValidated(int64_t offset, 320 int32_t bytes_to_read, 321 const PP_ArrayOutput& array_output, 322 scoped_refptr<TrackedCallback> callback) { 323 if (bytes_to_read < 0) 324 return PP_ERROR_FAILED; 325 if (!FileHandleHolder::IsValid(file_handle_)) 326 return PP_ERROR_FAILED; 327 328 state_manager_.SetPendingOperation(FileIOStateManager::OPERATION_READ); 329 330 bytes_to_read = std::min(bytes_to_read, kMaxReadSize); 331 if (callback->is_blocking()) { 332 char* buffer = static_cast<char*>( 333 array_output.GetDataBuffer(array_output.user_data, bytes_to_read, 1)); 334 int32_t result = PP_ERROR_FAILED; 335 // The plugin could release its reference to this instance when we release 336 // the proxy lock below. 337 scoped_refptr<FileIOResource> protect(this); 338 if (buffer) { 339 // Release the proxy lock while making a potentially slow file call. 340 ProxyAutoUnlock unlock; 341 result = base::ReadPlatformFile( 342 file_handle_->raw_handle(), offset, buffer, bytes_to_read); 343 if (result < 0) 344 result = PP_ERROR_FAILED; 345 } 346 state_manager_.SetOperationFinished(); 347 return result; 348 } 349 350 // For the non-blocking case, post a task to the file thread. 351 scoped_refptr<ReadOp> read_op( 352 new ReadOp(file_handle_, offset, bytes_to_read)); 353 base::PostTaskAndReplyWithResult( 354 PpapiGlobals::Get()->GetFileTaskRunner(), 355 FROM_HERE, 356 Bind(&FileIOResource::ReadOp::DoWork, read_op), 357 RunWhileLocked(Bind(&TrackedCallback::Run, callback))); 358 callback->set_completion_task( 359 Bind(&FileIOResource::OnReadComplete, this, read_op, array_output)); 360 361 return PP_OK_COMPLETIONPENDING; 362} 363 364int32_t FileIOResource::OnQueryComplete(scoped_refptr<QueryOp> query_op, 365 PP_FileInfo* info, 366 int32_t result) { 367 DCHECK(state_manager_.get_pending_operation() == 368 FileIOStateManager::OPERATION_EXCLUSIVE); 369 370 if (result == PP_OK) { 371 // This writes the file info into the plugin's PP_FileInfo struct. 372 ppapi::PlatformFileInfoToPepperFileInfo(query_op->file_info(), 373 file_system_type_, 374 info); 375 } 376 state_manager_.SetOperationFinished(); 377 return result; 378} 379 380int32_t FileIOResource::OnReadComplete(scoped_refptr<ReadOp> read_op, 381 PP_ArrayOutput array_output, 382 int32_t result) { 383 DCHECK(state_manager_.get_pending_operation() == 384 FileIOStateManager::OPERATION_READ); 385 if (result >= 0) { 386 ArrayWriter output; 387 output.set_pp_array_output(array_output); 388 if (output.is_valid()) 389 output.StoreArray(read_op->buffer(), result); 390 else 391 result = PP_ERROR_FAILED; 392 } else { 393 // The read operation failed. 394 result = PP_ERROR_FAILED; 395 } 396 state_manager_.SetOperationFinished(); 397 return result; 398} 399 400void FileIOResource::OnPluginMsgGeneralComplete( 401 scoped_refptr<TrackedCallback> callback, 402 const ResourceMessageReplyParams& params) { 403 DCHECK(state_manager_.get_pending_operation() == 404 FileIOStateManager::OPERATION_EXCLUSIVE || 405 state_manager_.get_pending_operation() == 406 FileIOStateManager::OPERATION_WRITE); 407 // End this operation now, so the user's callback can execute another FileIO 408 // operation, assuming there are no other pending operations. 409 state_manager_.SetOperationFinished(); 410 callback->Run(params.result()); 411} 412 413void FileIOResource::OnPluginMsgOpenFileComplete( 414 scoped_refptr<TrackedCallback> callback, 415 const ResourceMessageReplyParams& params) { 416 DCHECK(state_manager_.get_pending_operation() == 417 FileIOStateManager::OPERATION_EXCLUSIVE); 418 419 // Release the FileRef resource. 420 file_ref_ = NULL; 421 if (params.result() == PP_OK) 422 state_manager_.SetOpenSucceed(); 423 424 int32_t result = params.result(); 425 IPC::PlatformFileForTransit transit_file; 426 if ((result == PP_OK) && params.TakeFileHandleAtIndex(0, &transit_file)) { 427 file_handle_ = new FileHandleHolder( 428 IPC::PlatformFileForTransitToPlatformFile(transit_file)); 429 } 430 // End this operation now, so the user's callback can execute another FileIO 431 // operation, assuming there are no other pending operations. 432 state_manager_.SetOperationFinished(); 433 callback->Run(result); 434} 435 436void FileIOResource::OnPluginMsgRequestOSFileHandleComplete( 437 scoped_refptr<TrackedCallback> callback, 438 PP_FileHandle* output_handle, 439 const ResourceMessageReplyParams& params) { 440 DCHECK(state_manager_.get_pending_operation() == 441 FileIOStateManager::OPERATION_EXCLUSIVE); 442 443 if (!TrackedCallback::IsPending(callback)) { 444 state_manager_.SetOperationFinished(); 445 return; 446 } 447 448 int32_t result = params.result(); 449 IPC::PlatformFileForTransit transit_file; 450 if (!params.TakeFileHandleAtIndex(0, &transit_file)) 451 result = PP_ERROR_FAILED; 452 *output_handle = IPC::PlatformFileForTransitToPlatformFile(transit_file); 453 454 // End this operation now, so the user's callback can execute another FileIO 455 // operation, assuming there are no other pending operations. 456 state_manager_.SetOperationFinished(); 457 callback->Run(result); 458} 459 460} // namespace proxy 461} // namespace ppapi 462