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