file_io_resource.cc revision d0247b1b59f9c528cb6df88b4f2b9afaf80d181e
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(PP_FileHandle file_handle) 47 : file_handle_(file_handle) { 48} 49 50FileIOResource::QueryOp::~QueryOp() { 51} 52 53int32_t FileIOResource::QueryOp::DoWork() { 54 return base::GetPlatformFileInfo(file_handle_, &file_info_) ? 55 PP_OK : PP_ERROR_FAILED; 56} 57 58FileIOResource::ReadOp::ReadOp(PP_FileHandle file_handle, 59 int64_t offset, 60 int32_t bytes_to_read) 61 : file_handle_(file_handle), 62 offset_(offset), 63 bytes_to_read_(bytes_to_read) { 64} 65 66FileIOResource::ReadOp::~ReadOp() { 67} 68 69int32_t FileIOResource::ReadOp::DoWork() { 70 DCHECK(!buffer_.get()); 71 buffer_.reset(new char[bytes_to_read_]); 72 return base::ReadPlatformFile( 73 file_handle_, offset_, buffer_.get(), bytes_to_read_); 74} 75 76FileIOResource::FileIOResource(Connection connection, PP_Instance instance) 77 : PluginResource(connection, instance), 78 file_handle_(base::kInvalidPlatformFileValue), 79 file_system_type_(PP_FILESYSTEMTYPE_INVALID) { 80 SendCreate(RENDERER, PpapiHostMsg_FileIO_Create()); 81} 82 83FileIOResource::~FileIOResource() { 84 CloseFileHandle(); 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>(RENDERER, 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 (file_handle_ == base::kInvalidPlatformFileValue) 138 return PP_ERROR_FAILED; 139 140 state_manager_.SetPendingOperation(FileIOStateManager::OPERATION_EXCLUSIVE); 141 scoped_refptr<QueryOp> query_op(new QueryOp(file_handle_)); 142 143 // If the callback is blocking, perform the task on the calling thread. 144 if (callback->is_blocking()) { 145 int32_t result; 146 { 147 // Release the proxy lock while making a potentially slow file call. 148 ProxyAutoUnlock unlock; 149 result = query_op->DoWork(); 150 } 151 return OnQueryComplete(query_op, info, result); 152 } 153 154 // For the non-blocking case, post a task to the file thread and add a 155 // completion task to write the result. 156 base::PostTaskAndReplyWithResult( 157 PpapiGlobals::Get()->GetFileTaskRunner(pp_instance()), 158 FROM_HERE, 159 Bind(&FileIOResource::QueryOp::DoWork, query_op), 160 RunWhileLocked(Bind(&TrackedCallback::Run, callback))); 161 callback->set_completion_task( 162 Bind(&FileIOResource::OnQueryComplete, this, query_op, info)); 163 164 return PP_OK_COMPLETIONPENDING; 165} 166 167int32_t FileIOResource::Touch(PP_Time last_access_time, 168 PP_Time last_modified_time, 169 scoped_refptr<TrackedCallback> callback) { 170 int32_t rv = state_manager_.CheckOperationState( 171 FileIOStateManager::OPERATION_EXCLUSIVE, true); 172 if (rv != PP_OK) 173 return rv; 174 175 Call<PpapiPluginMsg_FileIO_GeneralReply>(RENDERER, 176 PpapiHostMsg_FileIO_Touch(last_access_time, last_modified_time), 177 base::Bind(&FileIOResource::OnPluginMsgGeneralComplete, this, 178 callback)); 179 180 state_manager_.SetPendingOperation(FileIOStateManager::OPERATION_EXCLUSIVE); 181 return PP_OK_COMPLETIONPENDING; 182} 183 184int32_t FileIOResource::Read(int64_t offset, 185 char* buffer, 186 int32_t bytes_to_read, 187 scoped_refptr<TrackedCallback> callback) { 188 int32_t rv = state_manager_.CheckOperationState( 189 FileIOStateManager::OPERATION_READ, true); 190 if (rv != PP_OK) 191 return rv; 192 193 PP_ArrayOutput output_adapter; 194 output_adapter.GetDataBuffer = &DummyGetDataBuffer; 195 output_adapter.user_data = buffer; 196 return ReadValidated(offset, bytes_to_read, output_adapter, callback); 197} 198 199int32_t FileIOResource::ReadToArray(int64_t offset, 200 int32_t max_read_length, 201 PP_ArrayOutput* array_output, 202 scoped_refptr<TrackedCallback> callback) { 203 DCHECK(array_output); 204 int32_t rv = state_manager_.CheckOperationState( 205 FileIOStateManager::OPERATION_READ, true); 206 if (rv != PP_OK) 207 return rv; 208 209 return ReadValidated(offset, max_read_length, *array_output, callback); 210} 211 212int32_t FileIOResource::Write(int64_t offset, 213 const char* buffer, 214 int32_t bytes_to_write, 215 scoped_refptr<TrackedCallback> callback) { 216 int32_t rv = state_manager_.CheckOperationState( 217 FileIOStateManager::OPERATION_WRITE, true); 218 if (rv != PP_OK) 219 return rv; 220 221 // TODO(brettw) it would be nice to use a shared memory buffer for large 222 // writes rather than having to copy to a string (which will involve a number 223 // of extra copies to serialize over IPC). 224 Call<PpapiPluginMsg_FileIO_GeneralReply>(RENDERER, 225 PpapiHostMsg_FileIO_Write(offset, std::string(buffer, bytes_to_write)), 226 base::Bind(&FileIOResource::OnPluginMsgGeneralComplete, this, 227 callback)); 228 229 state_manager_.SetPendingOperation(FileIOStateManager::OPERATION_WRITE); 230 return PP_OK_COMPLETIONPENDING; 231} 232 233int32_t FileIOResource::SetLength(int64_t length, 234 scoped_refptr<TrackedCallback> callback) { 235 int32_t rv = state_manager_.CheckOperationState( 236 FileIOStateManager::OPERATION_EXCLUSIVE, true); 237 if (rv != PP_OK) 238 return rv; 239 240 Call<PpapiPluginMsg_FileIO_GeneralReply>(RENDERER, 241 PpapiHostMsg_FileIO_SetLength(length), 242 base::Bind(&FileIOResource::OnPluginMsgGeneralComplete, this, 243 callback)); 244 245 state_manager_.SetPendingOperation(FileIOStateManager::OPERATION_EXCLUSIVE); 246 return PP_OK_COMPLETIONPENDING; 247} 248 249int32_t FileIOResource::Flush(scoped_refptr<TrackedCallback> callback) { 250 int32_t rv = state_manager_.CheckOperationState( 251 FileIOStateManager::OPERATION_EXCLUSIVE, true); 252 if (rv != PP_OK) 253 return rv; 254 255 Call<PpapiPluginMsg_FileIO_GeneralReply>(RENDERER, 256 PpapiHostMsg_FileIO_Flush(), 257 base::Bind(&FileIOResource::OnPluginMsgGeneralComplete, this, 258 callback)); 259 260 state_manager_.SetPendingOperation(FileIOStateManager::OPERATION_EXCLUSIVE); 261 return PP_OK_COMPLETIONPENDING; 262} 263 264void FileIOResource::Close() { 265 CloseFileHandle(); 266 Post(RENDERER, PpapiHostMsg_FileIO_Close()); 267} 268 269int32_t FileIOResource::GetOSFileDescriptor() { 270 int32_t file_descriptor; 271 // Only available when running in process. 272 SyncCall<PpapiPluginMsg_FileIO_GetOSFileDescriptorReply>( 273 RENDERER, PpapiHostMsg_FileIO_GetOSFileDescriptor(), &file_descriptor); 274 return file_descriptor; 275} 276 277int32_t FileIOResource::RequestOSFileHandle( 278 PP_FileHandle* handle, 279 scoped_refptr<TrackedCallback> callback) { 280 int32_t rv = state_manager_.CheckOperationState( 281 FileIOStateManager::OPERATION_EXCLUSIVE, true); 282 if (rv != PP_OK) 283 return rv; 284 285 Call<PpapiPluginMsg_FileIO_RequestOSFileHandleReply>(RENDERER, 286 PpapiHostMsg_FileIO_RequestOSFileHandle(), 287 base::Bind(&FileIOResource::OnPluginMsgRequestOSFileHandleComplete, this, 288 callback, handle)); 289 290 state_manager_.SetPendingOperation(FileIOStateManager::OPERATION_EXCLUSIVE); 291 return PP_OK_COMPLETIONPENDING; 292} 293 294int32_t FileIOResource::WillWrite(int64_t offset, 295 int32_t bytes_to_write, 296 scoped_refptr<TrackedCallback> callback) { 297 Call<PpapiPluginMsg_FileIO_GeneralReply>(RENDERER, 298 PpapiHostMsg_FileIO_WillWrite(offset, bytes_to_write), 299 base::Bind(&FileIOResource::OnPluginMsgGeneralComplete, this, callback)); 300 301 state_manager_.SetPendingOperation(FileIOStateManager::OPERATION_EXCLUSIVE); 302 return PP_OK_COMPLETIONPENDING; 303} 304 305int32_t FileIOResource::WillSetLength(int64_t length, 306 scoped_refptr<TrackedCallback> callback) { 307 Call<PpapiPluginMsg_FileIO_GeneralReply>(RENDERER, 308 PpapiHostMsg_FileIO_WillSetLength(length), 309 base::Bind(&FileIOResource::OnPluginMsgGeneralComplete, this, callback)); 310 311 state_manager_.SetPendingOperation(FileIOStateManager::OPERATION_EXCLUSIVE); 312 return PP_OK_COMPLETIONPENDING; 313} 314 315int32_t FileIOResource::ReadValidated(int64_t offset, 316 int32_t bytes_to_read, 317 const PP_ArrayOutput& array_output, 318 scoped_refptr<TrackedCallback> callback) { 319 if (bytes_to_read < 0) 320 return PP_ERROR_FAILED; 321 if (file_handle_ == base::kInvalidPlatformFileValue) 322 return PP_ERROR_FAILED; 323 324 state_manager_.SetPendingOperation(FileIOStateManager::OPERATION_READ); 325 326 bytes_to_read = std::min(bytes_to_read, kMaxReadSize); 327 scoped_refptr<ReadOp> read_op( 328 new ReadOp(file_handle_, offset, bytes_to_read)); 329 if (callback->is_blocking()) { 330 int32_t result; 331 { 332 // Release the proxy lock while making a potentially slow file call. 333 ProxyAutoUnlock unlock; 334 result = read_op->DoWork(); 335 } 336 return OnReadComplete(read_op, array_output, result); 337 } 338 339 // For the non-blocking case, post a task to the file thread. 340 base::PostTaskAndReplyWithResult( 341 PpapiGlobals::Get()->GetFileTaskRunner(pp_instance()), 342 FROM_HERE, 343 Bind(&FileIOResource::ReadOp::DoWork, read_op), 344 RunWhileLocked(Bind(&TrackedCallback::Run, callback))); 345 callback->set_completion_task( 346 Bind(&FileIOResource::OnReadComplete, this, read_op, array_output)); 347 348 return PP_OK_COMPLETIONPENDING; 349} 350 351void FileIOResource::CloseFileHandle() { 352 if (file_handle_ != base::kInvalidPlatformFileValue) { 353 // Close our local fd on the file thread. 354 base::TaskRunner* file_task_runner = 355 PpapiGlobals::Get()->GetFileTaskRunner(pp_instance()); 356 file_task_runner->PostTask(FROM_HERE, 357 base::Bind(&DoClose, file_handle_)); 358 359 file_handle_ = base::kInvalidPlatformFileValue; 360 } 361} 362 363int32_t FileIOResource::OnQueryComplete(scoped_refptr<QueryOp> query_op, 364 PP_FileInfo* info, 365 int32_t result) { 366 DCHECK(state_manager_.get_pending_operation() == 367 FileIOStateManager::OPERATION_EXCLUSIVE); 368 369 if (result == PP_OK) { 370 // This writes the file info into the plugin's PP_FileInfo struct. 371 ppapi::PlatformFileInfoToPepperFileInfo(query_op->file_info(), 372 file_system_type_, 373 info); 374 } 375 state_manager_.SetOperationFinished(); 376 return result; 377} 378 379int32_t FileIOResource::OnReadComplete(scoped_refptr<ReadOp> read_op, 380 PP_ArrayOutput array_output, 381 int32_t result) { 382 DCHECK(state_manager_.get_pending_operation() == 383 FileIOStateManager::OPERATION_READ); 384 if (result >= 0) { 385 ArrayWriter output; 386 output.set_pp_array_output(array_output); 387 if (output.is_valid()) 388 output.StoreArray(read_op->buffer(), result); 389 else 390 result = PP_ERROR_FAILED; 391 } else { 392 // The read operation failed. 393 result = PP_ERROR_FAILED; 394 } 395 state_manager_.SetOperationFinished(); 396 return result; 397} 398 399void FileIOResource::OnPluginMsgGeneralComplete( 400 scoped_refptr<TrackedCallback> callback, 401 const ResourceMessageReplyParams& params) { 402 DCHECK(state_manager_.get_pending_operation() == 403 FileIOStateManager::OPERATION_EXCLUSIVE || 404 state_manager_.get_pending_operation() == 405 FileIOStateManager::OPERATION_WRITE); 406 // End this operation now, so the user's callback can execute another FileIO 407 // operation, assuming there are no other pending operations. 408 state_manager_.SetOperationFinished(); 409 callback->Run(params.result()); 410} 411 412void FileIOResource::OnPluginMsgOpenFileComplete( 413 scoped_refptr<TrackedCallback> callback, 414 const ResourceMessageReplyParams& params) { 415 DCHECK(state_manager_.get_pending_operation() == 416 FileIOStateManager::OPERATION_EXCLUSIVE); 417 418 // Release the FileRef resource. 419 file_ref_ = NULL; 420 if (params.result() == PP_OK) 421 state_manager_.SetOpenSucceed(); 422 423 int32_t result = params.result(); 424 IPC::PlatformFileForTransit transit_file; 425 if ((result == PP_OK) && params.TakeFileHandleAtIndex(0, &transit_file)) 426 file_handle_ = IPC::PlatformFileForTransitToPlatformFile(transit_file); 427 // End this operation now, so the user's callback can execute another FileIO 428 // operation, assuming there are no other pending operations. 429 state_manager_.SetOperationFinished(); 430 callback->Run(result); 431} 432 433void FileIOResource::OnPluginMsgRequestOSFileHandleComplete( 434 scoped_refptr<TrackedCallback> callback, 435 PP_FileHandle* output_handle, 436 const ResourceMessageReplyParams& params) { 437 DCHECK(state_manager_.get_pending_operation() == 438 FileIOStateManager::OPERATION_EXCLUSIVE); 439 440 if (!TrackedCallback::IsPending(callback)) { 441 state_manager_.SetOperationFinished(); 442 return; 443 } 444 445 int32_t result = params.result(); 446 IPC::PlatformFileForTransit transit_file; 447 if (!params.TakeFileHandleAtIndex(0, &transit_file)) 448 result = PP_ERROR_FAILED; 449 *output_handle = IPC::PlatformFileForTransitToPlatformFile(transit_file); 450 451 // End this operation now, so the user's callback can execute another FileIO 452 // operation, assuming there are no other pending operations. 453 state_manager_.SetOperationFinished(); 454 callback->Run(result); 455} 456 457} // namespace proxy 458} // namespace ppapi 459