file_io_resource.cc revision fb250657ef40d7500f20882d5c9909c1013367d3
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/files/file_util_proxy.h" 9#include "ipc/ipc_message.h" 10#include "ppapi/c/pp_errors.h" 11#include "ppapi/proxy/plugin_globals.h" 12#include "ppapi/proxy/ppapi_messages.h" 13#include "ppapi/shared_impl/array_writer.h" 14#include "ppapi/shared_impl/file_type_conversion.h" 15#include "ppapi/shared_impl/ppapi_globals.h" 16#include "ppapi/shared_impl/proxy_lock.h" 17#include "ppapi/shared_impl/resource_tracker.h" 18#include "ppapi/thunk/enter.h" 19#include "ppapi/thunk/ppb_file_ref_api.h" 20 21using ppapi::thunk::EnterResourceNoLock; 22using ppapi::thunk::PPB_FileIO_API; 23using ppapi::thunk::PPB_FileRef_API; 24 25namespace { 26 27// An adapter to let Read() share the same implementation with ReadToArray(). 28void* DummyGetDataBuffer(void* user_data, uint32_t count, uint32_t size) { 29 return user_data; 30} 31 32// Dummy close callback allows us to call CloseFileHandle in the destructor. 33void DummyCloseCallback(base::PlatformFileError error_code) { 34} 35 36} // namespace 37 38namespace ppapi { 39namespace proxy { 40 41FileIOResource::FileIOResource(Connection connection, PP_Instance instance) 42 : PluginResource(connection, instance), 43 file_handle_(PP_kInvalidFileHandle), 44 file_system_type_(PP_FILESYSTEMTYPE_INVALID) { 45 SendCreate(RENDERER, PpapiHostMsg_FileIO_Create()); 46} 47 48FileIOResource::~FileIOResource() { 49 CloseFileHandle(); 50} 51 52PPB_FileIO_API* FileIOResource::AsPPB_FileIO_API() { 53 return this; 54} 55 56int32_t FileIOResource::Open(PP_Resource file_ref, 57 int32_t open_flags, 58 scoped_refptr<TrackedCallback> callback) { 59 EnterResourceNoLock<PPB_FileRef_API> enter(file_ref, true); 60 if (enter.failed()) 61 return PP_ERROR_BADRESOURCE; 62 63 PPB_FileRef_API* file_ref_api = enter.object(); 64 PP_FileSystemType type = file_ref_api->GetFileSystemType(); 65 if (type != PP_FILESYSTEMTYPE_LOCALPERSISTENT && 66 type != PP_FILESYSTEMTYPE_LOCALTEMPORARY && 67 type != PP_FILESYSTEMTYPE_EXTERNAL && 68 type != PP_FILESYSTEMTYPE_ISOLATED) { 69 NOTREACHED(); 70 return PP_ERROR_FAILED; 71 } 72 file_system_type_ = type; 73 74 int32_t rv = state_manager_.CheckOperationState( 75 FileIOStateManager::OPERATION_EXCLUSIVE, false); 76 if (rv != PP_OK) 77 return rv; 78 79 Call<PpapiPluginMsg_FileIO_OpenReply>(RENDERER, 80 PpapiHostMsg_FileIO_Open( 81 enter.resource()->host_resource().host_resource(), 82 open_flags), 83 base::Bind(&FileIOResource::OnPluginMsgOpenFileComplete, this, 84 callback)); 85 86 state_manager_.SetPendingOperation(FileIOStateManager::OPERATION_EXCLUSIVE); 87 return PP_OK_COMPLETIONPENDING; 88} 89 90int32_t FileIOResource::Query(PP_FileInfo* info, 91 scoped_refptr<TrackedCallback> callback) { 92 int32_t rv = state_manager_.CheckOperationState( 93 FileIOStateManager::OPERATION_EXCLUSIVE, true); 94 if (rv != PP_OK) 95 return rv; 96 97 if (file_handle_ == base::kInvalidPlatformFileValue) 98 return PP_ERROR_FAILED; 99 100 if (!base::FileUtilProxy::GetFileInfoFromPlatformFile( 101 PpapiGlobals::Get()->GetFileTaskRunner(pp_instance()), 102 file_handle_, 103 RunWhileLocked(base::Bind(&FileIOResource::OnQueryComplete, this, 104 callback, info)))) { 105 return PP_ERROR_FAILED; 106 } 107 108 state_manager_.SetPendingOperation(FileIOStateManager::OPERATION_EXCLUSIVE); 109 return PP_OK_COMPLETIONPENDING; 110} 111 112int32_t FileIOResource::Touch(PP_Time last_access_time, 113 PP_Time last_modified_time, 114 scoped_refptr<TrackedCallback> callback) { 115 int32_t rv = state_manager_.CheckOperationState( 116 FileIOStateManager::OPERATION_EXCLUSIVE, true); 117 if (rv != PP_OK) 118 return rv; 119 120 Call<PpapiPluginMsg_FileIO_GeneralReply>(RENDERER, 121 PpapiHostMsg_FileIO_Touch(last_access_time, last_modified_time), 122 base::Bind(&FileIOResource::OnPluginMsgGeneralComplete, this, 123 callback)); 124 125 state_manager_.SetPendingOperation(FileIOStateManager::OPERATION_EXCLUSIVE); 126 return PP_OK_COMPLETIONPENDING; 127} 128 129int32_t FileIOResource::Read(int64_t offset, 130 char* buffer, 131 int32_t bytes_to_read, 132 scoped_refptr<TrackedCallback> callback) { 133 int32_t rv = state_manager_.CheckOperationState( 134 FileIOStateManager::OPERATION_READ, true); 135 if (rv != PP_OK) 136 return rv; 137 138 PP_ArrayOutput output_adapter; 139 output_adapter.GetDataBuffer = &DummyGetDataBuffer; 140 output_adapter.user_data = buffer; 141 return ReadValidated(offset, bytes_to_read, output_adapter, callback); 142} 143 144int32_t FileIOResource::ReadToArray(int64_t offset, 145 int32_t max_read_length, 146 PP_ArrayOutput* array_output, 147 scoped_refptr<TrackedCallback> callback) { 148 DCHECK(array_output); 149 int32_t rv = state_manager_.CheckOperationState( 150 FileIOStateManager::OPERATION_READ, true); 151 if (rv != PP_OK) 152 return rv; 153 154 return ReadValidated(offset, max_read_length, *array_output, callback); 155} 156 157int32_t FileIOResource::Write(int64_t offset, 158 const char* buffer, 159 int32_t bytes_to_write, 160 scoped_refptr<TrackedCallback> callback) { 161 int32_t rv = state_manager_.CheckOperationState( 162 FileIOStateManager::OPERATION_WRITE, true); 163 if (rv != PP_OK) 164 return rv; 165 166 // TODO(brettw) it would be nice to use a shared memory buffer for large 167 // writes rather than having to copy to a string (which will involve a number 168 // of extra copies to serialize over IPC). 169 Call<PpapiPluginMsg_FileIO_GeneralReply>(RENDERER, 170 PpapiHostMsg_FileIO_Write(offset, std::string(buffer, bytes_to_write)), 171 base::Bind(&FileIOResource::OnPluginMsgGeneralComplete, this, 172 callback)); 173 174 state_manager_.SetPendingOperation(FileIOStateManager::OPERATION_WRITE); 175 return PP_OK_COMPLETIONPENDING; 176} 177 178int32_t FileIOResource::SetLength(int64_t length, 179 scoped_refptr<TrackedCallback> callback) { 180 int32_t rv = state_manager_.CheckOperationState( 181 FileIOStateManager::OPERATION_EXCLUSIVE, true); 182 if (rv != PP_OK) 183 return rv; 184 185 Call<PpapiPluginMsg_FileIO_GeneralReply>(RENDERER, 186 PpapiHostMsg_FileIO_SetLength(length), 187 base::Bind(&FileIOResource::OnPluginMsgGeneralComplete, this, 188 callback)); 189 190 state_manager_.SetPendingOperation(FileIOStateManager::OPERATION_EXCLUSIVE); 191 return PP_OK_COMPLETIONPENDING; 192} 193 194int32_t FileIOResource::Flush(scoped_refptr<TrackedCallback> callback) { 195 int32_t rv = state_manager_.CheckOperationState( 196 FileIOStateManager::OPERATION_EXCLUSIVE, true); 197 if (rv != PP_OK) 198 return rv; 199 200 Call<PpapiPluginMsg_FileIO_GeneralReply>(RENDERER, 201 PpapiHostMsg_FileIO_Flush(), 202 base::Bind(&FileIOResource::OnPluginMsgGeneralComplete, this, 203 callback)); 204 205 state_manager_.SetPendingOperation(FileIOStateManager::OPERATION_EXCLUSIVE); 206 return PP_OK_COMPLETIONPENDING; 207} 208 209void FileIOResource::Close() { 210 CloseFileHandle(); 211 Post(RENDERER, PpapiHostMsg_FileIO_Close()); 212} 213 214int32_t FileIOResource::GetOSFileDescriptor() { 215 int32_t file_descriptor; 216 // Only available when running in process. 217 SyncCall<PpapiPluginMsg_FileIO_GetOSFileDescriptorReply>( 218 RENDERER, PpapiHostMsg_FileIO_GetOSFileDescriptor(), &file_descriptor); 219 return file_descriptor; 220} 221 222int32_t FileIOResource::RequestOSFileHandle( 223 PP_FileHandle* handle, 224 scoped_refptr<TrackedCallback> callback) { 225 int32_t rv = state_manager_.CheckOperationState( 226 FileIOStateManager::OPERATION_EXCLUSIVE, true); 227 if (rv != PP_OK) 228 return rv; 229 230 Call<PpapiPluginMsg_FileIO_RequestOSFileHandleReply>(RENDERER, 231 PpapiHostMsg_FileIO_RequestOSFileHandle(), 232 base::Bind(&FileIOResource::OnPluginMsgRequestOSFileHandleComplete, this, 233 callback, handle)); 234 235 state_manager_.SetPendingOperation(FileIOStateManager::OPERATION_EXCLUSIVE); 236 return PP_OK_COMPLETIONPENDING; 237} 238 239int32_t FileIOResource::WillWrite(int64_t offset, 240 int32_t bytes_to_write, 241 scoped_refptr<TrackedCallback> callback) { 242 Call<PpapiPluginMsg_FileIO_GeneralReply>(RENDERER, 243 PpapiHostMsg_FileIO_WillWrite(offset, bytes_to_write), 244 base::Bind(&FileIOResource::OnPluginMsgGeneralComplete, this, 245 callback)); 246 247 state_manager_.SetPendingOperation(FileIOStateManager::OPERATION_EXCLUSIVE); 248 return PP_OK_COMPLETIONPENDING; 249} 250 251int32_t FileIOResource::WillSetLength(int64_t length, 252 scoped_refptr<TrackedCallback> callback) { 253 Call<PpapiPluginMsg_FileIO_GeneralReply>(RENDERER, 254 PpapiHostMsg_FileIO_WillSetLength(length), 255 base::Bind(&FileIOResource::OnPluginMsgGeneralComplete, this, 256 callback)); 257 258 state_manager_.SetPendingOperation(FileIOStateManager::OPERATION_EXCLUSIVE); 259 return PP_OK_COMPLETIONPENDING; 260} 261 262int32_t FileIOResource::ReadValidated(int64_t offset, 263 int32_t bytes_to_read, 264 const PP_ArrayOutput& array_output, 265 scoped_refptr<TrackedCallback> callback) { 266 if (file_handle_ == base::kInvalidPlatformFileValue) 267 return PP_ERROR_FAILED; 268 269 if (!base::FileUtilProxy::Read( 270 PpapiGlobals::Get()->GetFileTaskRunner(pp_instance()), 271 file_handle_, 272 offset, 273 bytes_to_read, 274 RunWhileLocked(base::Bind(&FileIOResource::OnReadComplete, this, 275 callback, array_output)))) { 276 return PP_ERROR_FAILED; 277 } 278 279 state_manager_.SetPendingOperation(FileIOStateManager::OPERATION_READ); 280 return PP_OK_COMPLETIONPENDING; 281} 282 283void FileIOResource::CloseFileHandle() { 284 if (file_handle_ != base::kInvalidPlatformFileValue) { 285 base::FileUtilProxy::Close( 286 PpapiGlobals::Get()->GetFileTaskRunner(pp_instance()), 287 file_handle_, 288 base::Bind(&DummyCloseCallback)); 289 file_handle_ = base::kInvalidPlatformFileValue; 290 } 291} 292 293void FileIOResource::OnQueryComplete( 294 scoped_refptr<TrackedCallback> callback, 295 PP_FileInfo* output_info, 296 base::PlatformFileError error_code, 297 const base::PlatformFileInfo& file_info) { 298 DCHECK(state_manager_.get_pending_operation() == 299 FileIOStateManager::OPERATION_EXCLUSIVE); 300 301 if (!TrackedCallback::IsPending(callback)) { 302 state_manager_.SetOperationFinished(); 303 return; 304 } 305 306 int32_t result = ::ppapi::PlatformFileErrorToPepperError(error_code); 307 if (result == PP_OK) { 308 ppapi::PlatformFileInfoToPepperFileInfo(file_info, file_system_type_, 309 output_info); 310 } 311 312 // End this operation now, so the user's callback can execute another FileIO 313 // operation, assuming there are no other pending operations. 314 state_manager_.SetOperationFinished(); 315 callback->Run(result); 316} 317 318void FileIOResource::OnReadComplete( 319 scoped_refptr<TrackedCallback> callback, 320 PP_ArrayOutput array_output, 321 base::PlatformFileError error_code, 322 const char* data, int bytes_read) { 323 DCHECK(state_manager_.get_pending_operation() == 324 FileIOStateManager::OPERATION_READ); 325 326 if (!TrackedCallback::IsPending(callback)) { 327 state_manager_.SetOperationFinished(); 328 return; 329 } 330 331 int32_t result = ::ppapi::PlatformFileErrorToPepperError(error_code); 332 if (result == PP_OK) { 333 result = std::max(0, bytes_read); 334 ArrayWriter output; 335 output.set_pp_array_output(array_output); 336 if (output.is_valid()) 337 output.StoreArray(data, result); 338 else 339 result = PP_ERROR_FAILED; 340 } 341 342 // End this operation now, so the user's callback can execute another FileIO 343 // operation, assuming there are no other pending operations. 344 state_manager_.SetOperationFinished(); 345 callback->Run(result); 346} 347 348void FileIOResource::OnPluginMsgGeneralComplete( 349 scoped_refptr<TrackedCallback> callback, 350 const ResourceMessageReplyParams& params) { 351 DCHECK(state_manager_.get_pending_operation() == 352 FileIOStateManager::OPERATION_EXCLUSIVE || 353 state_manager_.get_pending_operation() == 354 FileIOStateManager::OPERATION_WRITE); 355 // End this operation now, so the user's callback can execute another FileIO 356 // operation, assuming there are no other pending operations. 357 state_manager_.SetOperationFinished(); 358 callback->Run(params.result()); 359} 360 361void FileIOResource::OnPluginMsgOpenFileComplete( 362 scoped_refptr<TrackedCallback> callback, 363 const ResourceMessageReplyParams& params) { 364 DCHECK(state_manager_.get_pending_operation() == 365 FileIOStateManager::OPERATION_EXCLUSIVE); 366 if (params.result() == PP_OK) 367 state_manager_.SetOpenSucceed(); 368 369 int32_t result = params.result(); 370 IPC::PlatformFileForTransit transit_file; 371 if (result == PP_OK && !params.TakeFileHandleAtIndex(0, &transit_file)) 372 result = PP_ERROR_FAILED; 373 file_handle_ = IPC::PlatformFileForTransitToPlatformFile(transit_file); 374 375 // End this operation now, so the user's callback can execute another FileIO 376 // operation, assuming there are no other pending operations. 377 state_manager_.SetOperationFinished(); 378 callback->Run(params.result()); 379} 380 381void FileIOResource::OnPluginMsgRequestOSFileHandleComplete( 382 scoped_refptr<TrackedCallback> callback, 383 PP_FileHandle* output_handle, 384 const ResourceMessageReplyParams& params) { 385 DCHECK(state_manager_.get_pending_operation() == 386 FileIOStateManager::OPERATION_EXCLUSIVE); 387 388 if (!TrackedCallback::IsPending(callback)) { 389 state_manager_.SetOperationFinished(); 390 return; 391 } 392 393 int32_t result = params.result(); 394 IPC::PlatformFileForTransit transit_file; 395 if (!params.TakeFileHandleAtIndex(0, &transit_file)) 396 result = PP_ERROR_FAILED; 397 *output_handle = IPC::PlatformFileForTransitToPlatformFile(transit_file); 398 399 // End this operation now, so the user's callback can execute another FileIO 400 // operation, assuming there are no other pending operations. 401 state_manager_.SetOperationFinished(); 402 callback->Run(result); 403} 404 405} // namespace proxy 406} // namespace ppapi 407