1 // Copyright (c) 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 // MTPDeviceDelegateImplWin implementation. 6 7 #include "chrome/browser/media_galleries/win/mtp_device_delegate_impl_win.h" 8 9 #include <portabledevice.h> 10 11 #include <vector> 12 13 #include "base/bind.h" 14 #include "base/files/file_path.h" 15 #include "base/logging.h" 16 #include "base/memory/ref_counted.h" 17 #include "base/sequenced_task_runner.h" 18 #include "base/stl_util.h" 19 #include "base/strings/string_split.h" 20 #include "base/strings/string_util.h" 21 #include "base/strings/utf_string_conversions.h" 22 #include "base/task_runner_util.h" 23 #include "base/threading/thread_restrictions.h" 24 #include "chrome/browser/media_galleries/fileapi/media_file_system_backend.h" 25 #include "chrome/browser/media_galleries/win/mtp_device_object_entry.h" 26 #include "chrome/browser/media_galleries/win/mtp_device_object_enumerator.h" 27 #include "chrome/browser/media_galleries/win/mtp_device_operations_util.h" 28 #include "chrome/browser/media_galleries/win/portable_device_map_service.h" 29 #include "chrome/browser/media_galleries/win/snapshot_file_details.h" 30 #include "chrome/browser/storage_monitor/storage_monitor.h" 31 #include "content/public/browser/browser_thread.h" 32 #include "webkit/common/fileapi/file_system_util.h" 33 34 namespace chrome { 35 36 namespace { 37 38 // Gets the details of the MTP partition storage specified by the 39 // |storage_path| on the UI thread. Returns true if the storage details are 40 // valid and returns false otherwise. 41 bool GetStorageInfoOnUIThread(const string16& storage_path, 42 string16* pnp_device_id, 43 string16* storage_object_id) { 44 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); 45 DCHECK(!storage_path.empty()); 46 DCHECK(pnp_device_id); 47 DCHECK(storage_object_id); 48 string16 storage_device_id; 49 RemoveChars(storage_path, L"\\\\", &storage_device_id); 50 DCHECK(!storage_device_id.empty()); 51 // TODO(gbillock): Take the StorageMonitor as an argument. 52 StorageMonitor* monitor = StorageMonitor::GetInstance(); 53 DCHECK(monitor); 54 return monitor->GetMTPStorageInfoFromDeviceId( 55 UTF16ToUTF8(storage_device_id), pnp_device_id, storage_object_id); 56 } 57 58 // Returns the object id of the file object specified by the |file_path|, 59 // e.g. if the |file_path| is "\\MTP:StorageSerial:SID-{1001,,192}:125\DCIM" 60 // and |device_info.registered_device_path_| is 61 // "\\MTP:StorageSerial:SID-{1001,,192}:125", this function returns the 62 // identifier of the "DCIM" folder object. 63 // 64 // Returns an empty string if the device is detached while the request is in 65 // progress or when the |file_path| is invalid. 66 string16 GetFileObjectIdFromPathOnBlockingPoolThread( 67 const MTPDeviceDelegateImplWin::StorageDeviceInfo& device_info, 68 const base::FilePath& file_path) { 69 base::ThreadRestrictions::AssertIOAllowed(); 70 DCHECK(!file_path.empty()); 71 IPortableDevice* device = 72 PortableDeviceMapService::GetInstance()->GetPortableDevice( 73 device_info.registered_device_path); 74 if (!device) 75 return string16(); 76 77 if (device_info.registered_device_path == file_path.value()) 78 return device_info.storage_object_id; 79 80 base::FilePath relative_path; 81 if (!base::FilePath(device_info.registered_device_path).AppendRelativePath( 82 file_path, &relative_path)) 83 return string16(); 84 85 std::vector<string16> path_components; 86 relative_path.GetComponents(&path_components); 87 DCHECK(!path_components.empty()); 88 string16 parent_id(device_info.storage_object_id); 89 string16 file_object_id; 90 for (size_t i = 0; i < path_components.size(); ++i) { 91 file_object_id = 92 media_transfer_protocol::GetObjectIdFromName(device, parent_id, 93 path_components[i]); 94 if (file_object_id.empty()) 95 break; 96 parent_id = file_object_id; 97 } 98 return file_object_id; 99 } 100 101 // Returns a pointer to a new instance of AbstractFileEnumerator for the given 102 // |root| directory. Called on a blocking pool thread. 103 scoped_ptr<fileapi::FileSystemFileUtil::AbstractFileEnumerator> 104 CreateFileEnumeratorOnBlockingPoolThread( 105 const MTPDeviceDelegateImplWin::StorageDeviceInfo& device_info, 106 const base::FilePath& root) { 107 base::ThreadRestrictions::AssertIOAllowed(); 108 DCHECK(!device_info.registered_device_path.empty()); 109 DCHECK(!root.empty()); 110 scoped_ptr<fileapi::FileSystemFileUtil::AbstractFileEnumerator> 111 file_enumerator(new fileapi::FileSystemFileUtil::EmptyFileEnumerator()); 112 IPortableDevice* device = 113 PortableDeviceMapService::GetInstance()->GetPortableDevice( 114 device_info.registered_device_path); 115 if (!device) 116 return file_enumerator.Pass(); 117 118 string16 object_id = GetFileObjectIdFromPathOnBlockingPoolThread(device_info, 119 root); 120 if (object_id.empty()) 121 return file_enumerator.Pass(); 122 123 MTPDeviceObjectEntries entries; 124 if (!media_transfer_protocol::GetDirectoryEntries(device, object_id, 125 &entries) || 126 entries.empty()) 127 return file_enumerator.Pass(); 128 129 file_enumerator.reset(new MTPDeviceObjectEnumerator(entries)); 130 return file_enumerator.Pass(); 131 } 132 133 // Opens the device for communication on a blocking pool thread. 134 // |pnp_device_id| specifies the PnP device id. 135 // |registered_device_path| specifies the registered file system root path for 136 // the given device. 137 bool OpenDeviceOnBlockingPoolThread(const string16& pnp_device_id, 138 const string16& registered_device_path) { 139 base::ThreadRestrictions::AssertIOAllowed(); 140 DCHECK(!pnp_device_id.empty()); 141 DCHECK(!registered_device_path.empty()); 142 base::win::ScopedComPtr<IPortableDevice> device = 143 media_transfer_protocol::OpenDevice(pnp_device_id); 144 bool init_succeeded = device.get() != NULL; 145 if (init_succeeded) { 146 PortableDeviceMapService::GetInstance()->AddPortableDevice( 147 registered_device_path, device.get()); 148 } 149 return init_succeeded; 150 } 151 152 // Gets the |file_path| details from the MTP device specified by the 153 // |device_info.registered_device_path|. On success, |error| is set to 154 // base::PLATFORM_FILE_OK and fills in |file_info|. On failure, |error| is set 155 // to corresponding platform file error and |file_info| is not set. 156 base::PlatformFileError GetFileInfoOnBlockingPoolThread( 157 const MTPDeviceDelegateImplWin::StorageDeviceInfo& device_info, 158 const base::FilePath& file_path, 159 base::PlatformFileInfo* file_info) { 160 base::ThreadRestrictions::AssertIOAllowed(); 161 DCHECK(!device_info.registered_device_path.empty()); 162 DCHECK(!file_path.empty()); 163 DCHECK(file_info); 164 IPortableDevice* device = 165 PortableDeviceMapService::GetInstance()->GetPortableDevice( 166 device_info.registered_device_path); 167 if (!device) 168 return base::PLATFORM_FILE_ERROR_FAILED; 169 170 string16 object_id = GetFileObjectIdFromPathOnBlockingPoolThread(device_info, 171 file_path); 172 if (object_id.empty()) 173 return base::PLATFORM_FILE_ERROR_FAILED; 174 return media_transfer_protocol::GetFileEntryInfo(device, object_id, 175 file_info); 176 } 177 178 // Reads the |root| directory file entries on a blocking pool thread. On 179 // success, |error| is set to base::PLATFORM_FILE_OK and |entries| contains the 180 // directory file entries. On failure, |error| is set to platform file error 181 // and |entries| is not set. 182 base::PlatformFileError ReadDirectoryOnBlockingPoolThread( 183 const MTPDeviceDelegateImplWin::StorageDeviceInfo& device_info, 184 const base::FilePath& root, 185 fileapi::AsyncFileUtil::EntryList* entries) { 186 base::ThreadRestrictions::AssertIOAllowed(); 187 DCHECK(!root.empty()); 188 DCHECK(entries); 189 base::PlatformFileInfo file_info; 190 base::PlatformFileError error = GetFileInfoOnBlockingPoolThread(device_info, 191 root, 192 &file_info); 193 if (error != base::PLATFORM_FILE_OK) 194 return error; 195 196 if (!file_info.is_directory) 197 return base::PLATFORM_FILE_ERROR_NOT_A_DIRECTORY; 198 199 base::FilePath current; 200 scoped_ptr<fileapi::FileSystemFileUtil::AbstractFileEnumerator> file_enum = 201 CreateFileEnumeratorOnBlockingPoolThread(device_info, root); 202 while (!(current = file_enum->Next()).empty()) { 203 fileapi::DirectoryEntry entry; 204 entry.is_directory = file_enum->IsDirectory(); 205 entry.name = fileapi::VirtualPath::BaseName(current).value(); 206 entry.size = file_enum->Size(); 207 entry.last_modified_time = file_enum->LastModifiedTime(); 208 entries->push_back(entry); 209 } 210 return error; 211 } 212 213 // Gets the device file stream object on a blocking pool thread. 214 // |device_info| contains the device storage partition details. 215 // On success, returns base::PLATFORM_FILE_OK and file stream details are set in 216 // |file_details|. On failure, returns a platform file error and file stream 217 // details are not set in |file_details|. 218 base::PlatformFileError GetFileStreamOnBlockingPoolThread( 219 const MTPDeviceDelegateImplWin::StorageDeviceInfo& device_info, 220 SnapshotFileDetails* file_details) { 221 base::ThreadRestrictions::AssertIOAllowed(); 222 DCHECK(file_details); 223 DCHECK(!file_details->request_info().device_file_path.empty()); 224 DCHECK(!file_details->request_info().snapshot_file_path.empty()); 225 IPortableDevice* device = 226 PortableDeviceMapService::GetInstance()->GetPortableDevice( 227 device_info.registered_device_path); 228 if (!device) 229 return base::PLATFORM_FILE_ERROR_FAILED; 230 231 string16 file_object_id = 232 GetFileObjectIdFromPathOnBlockingPoolThread( 233 device_info, file_details->request_info().device_file_path); 234 if (file_object_id.empty()) 235 return base::PLATFORM_FILE_ERROR_FAILED; 236 237 base::PlatformFileInfo file_info; 238 base::PlatformFileError error = 239 GetFileInfoOnBlockingPoolThread( 240 device_info, 241 file_details->request_info().device_file_path, 242 &file_info); 243 if (error != base::PLATFORM_FILE_OK) 244 return error; 245 246 DWORD optimal_transfer_size = 0; 247 base::win::ScopedComPtr<IStream> file_stream; 248 if (file_info.size > 0) { 249 HRESULT hr = media_transfer_protocol::GetFileStreamForObject( 250 device, 251 file_object_id, 252 file_stream.Receive(), 253 &optimal_transfer_size); 254 if (hr != S_OK) 255 return base::PLATFORM_FILE_ERROR_FAILED; 256 } 257 258 // LocalFileStreamReader is used to read the contents of the snapshot file. 259 // Snapshot file modification time does not match the last modified time 260 // of the original media file. Therefore, set the last modified time to null 261 // in order to avoid the verification in LocalFileStreamReader. 262 // 263 // Users will use HTML5 FileSystem Entry getMetadata() interface to get the 264 // actual last modified time of the media file. 265 file_info.last_modified = base::Time(); 266 267 DCHECK(file_info.size == 0 || optimal_transfer_size > 0U); 268 file_details->set_file_info(file_info); 269 file_details->set_device_file_stream(file_stream); 270 file_details->set_optimal_transfer_size(optimal_transfer_size); 271 return error; 272 } 273 274 // Copies the data chunk from device file to the snapshot file based on the 275 // parameters specified by |file_details|. 276 // Returns the total number of bytes written to the snapshot file for non-empty 277 // files, or 0 on failure. For empty files, just return 0. 278 DWORD WriteDataChunkIntoSnapshotFileOnBlockingPoolThread( 279 const SnapshotFileDetails& file_details) { 280 base::ThreadRestrictions::AssertIOAllowed(); 281 if (file_details.file_info().size == 0) 282 return 0; 283 return media_transfer_protocol::CopyDataChunkToLocalFile( 284 file_details.device_file_stream(), 285 file_details.request_info().snapshot_file_path, 286 file_details.optimal_transfer_size()); 287 } 288 289 void DeletePortableDeviceOnBlockingPoolThread( 290 const string16& registered_device_path) { 291 base::ThreadRestrictions::AssertIOAllowed(); 292 PortableDeviceMapService::GetInstance()->RemovePortableDevice( 293 registered_device_path); 294 } 295 296 297 } // namespace 298 299 // Used by CreateMTPDeviceAsyncDelegate() to create the MTP device 300 // delegate on the IO thread. 301 void OnGetStorageInfoCreateDelegate( 302 const string16& device_location, 303 const CreateMTPDeviceAsyncDelegateCallback& callback, 304 string16* pnp_device_id, 305 string16* storage_object_id, 306 bool succeeded) { 307 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO)); 308 DCHECK(pnp_device_id); 309 DCHECK(storage_object_id); 310 if (!succeeded) 311 return; 312 callback.Run(new MTPDeviceDelegateImplWin(device_location, 313 *pnp_device_id, 314 *storage_object_id)); 315 } 316 317 void CreateMTPDeviceAsyncDelegate( 318 const string16& device_location, 319 const CreateMTPDeviceAsyncDelegateCallback& callback) { 320 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO)); 321 DCHECK(!device_location.empty()); 322 string16* pnp_device_id = new string16; 323 string16* storage_object_id = new string16; 324 content::BrowserThread::PostTaskAndReplyWithResult<bool>( 325 content::BrowserThread::UI, 326 FROM_HERE, 327 base::Bind(&GetStorageInfoOnUIThread, 328 device_location, 329 base::Unretained(pnp_device_id), 330 base::Unretained(storage_object_id)), 331 base::Bind(&OnGetStorageInfoCreateDelegate, 332 device_location, 333 callback, 334 base::Owned(pnp_device_id), 335 base::Owned(storage_object_id))); 336 } 337 338 // MTPDeviceDelegateImplWin --------------------------------------------------- 339 340 MTPDeviceDelegateImplWin::StorageDeviceInfo::StorageDeviceInfo( 341 const string16& pnp_device_id, 342 const string16& registered_device_path, 343 const string16& storage_object_id) 344 : pnp_device_id(pnp_device_id), 345 registered_device_path(registered_device_path), 346 storage_object_id(storage_object_id) { 347 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO)); 348 } 349 350 MTPDeviceDelegateImplWin::PendingTaskInfo::PendingTaskInfo( 351 const tracked_objects::Location& location, 352 const base::Callback<base::PlatformFileError(void)>& task, 353 const base::Callback<void(base::PlatformFileError)>& reply) 354 : location(location), 355 task(task), 356 reply(reply) { 357 } 358 359 MTPDeviceDelegateImplWin::MTPDeviceDelegateImplWin( 360 const string16& registered_device_path, 361 const string16& pnp_device_id, 362 const string16& storage_object_id) 363 : storage_device_info_(pnp_device_id, registered_device_path, 364 storage_object_id), 365 init_state_(UNINITIALIZED), 366 media_task_runner_(MediaFileSystemBackend::MediaTaskRunner()), 367 task_in_progress_(false), 368 weak_ptr_factory_(this) { 369 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO)); 370 DCHECK(!registered_device_path.empty()); 371 DCHECK(!pnp_device_id.empty()); 372 DCHECK(!storage_object_id.empty()); 373 DCHECK(media_task_runner_.get()); 374 } 375 376 MTPDeviceDelegateImplWin::~MTPDeviceDelegateImplWin() { 377 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO)); 378 } 379 380 void MTPDeviceDelegateImplWin::GetFileInfo( 381 const base::FilePath& file_path, 382 const GetFileInfoSuccessCallback& success_callback, 383 const ErrorCallback& error_callback) { 384 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO)); 385 DCHECK(!file_path.empty()); 386 base::PlatformFileInfo* file_info = new base::PlatformFileInfo; 387 EnsureInitAndRunTask( 388 PendingTaskInfo(FROM_HERE, 389 base::Bind(&GetFileInfoOnBlockingPoolThread, 390 storage_device_info_, 391 file_path, 392 base::Unretained(file_info)), 393 base::Bind(&MTPDeviceDelegateImplWin::OnGetFileInfo, 394 weak_ptr_factory_.GetWeakPtr(), 395 success_callback, 396 error_callback, 397 base::Owned(file_info)))); 398 } 399 400 void MTPDeviceDelegateImplWin::ReadDirectory( 401 const base::FilePath& root, 402 const ReadDirectorySuccessCallback& success_callback, 403 const ErrorCallback& error_callback) { 404 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO)); 405 DCHECK(!root.empty()); 406 fileapi::AsyncFileUtil::EntryList* entries = 407 new fileapi::AsyncFileUtil::EntryList; 408 EnsureInitAndRunTask( 409 PendingTaskInfo(FROM_HERE, 410 base::Bind(&ReadDirectoryOnBlockingPoolThread, 411 storage_device_info_, 412 root, 413 base::Unretained(entries)), 414 base::Bind(&MTPDeviceDelegateImplWin::OnDidReadDirectory, 415 weak_ptr_factory_.GetWeakPtr(), 416 success_callback, 417 error_callback, 418 base::Owned(entries)))); 419 } 420 421 void MTPDeviceDelegateImplWin::CreateSnapshotFile( 422 const base::FilePath& device_file_path, 423 const base::FilePath& snapshot_file_path, 424 const CreateSnapshotFileSuccessCallback& success_callback, 425 const ErrorCallback& error_callback) { 426 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO)); 427 DCHECK(!device_file_path.empty()); 428 DCHECK(!snapshot_file_path.empty()); 429 scoped_ptr<SnapshotFileDetails> file_details( 430 new SnapshotFileDetails(SnapshotRequestInfo(device_file_path, 431 snapshot_file_path, 432 success_callback, 433 error_callback))); 434 // Passing a raw SnapshotFileDetails* to the blocking pool is safe, because 435 // it is owned by |file_details| in the reply callback. 436 EnsureInitAndRunTask( 437 PendingTaskInfo(FROM_HERE, 438 base::Bind(&GetFileStreamOnBlockingPoolThread, 439 storage_device_info_, 440 file_details.get()), 441 base::Bind(&MTPDeviceDelegateImplWin::OnGetFileStream, 442 weak_ptr_factory_.GetWeakPtr(), 443 base::Passed(&file_details)))); 444 } 445 446 void MTPDeviceDelegateImplWin::CancelPendingTasksAndDeleteDelegate() { 447 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO)); 448 PortableDeviceMapService::GetInstance()->MarkPortableDeviceForDeletion( 449 storage_device_info_.registered_device_path); 450 media_task_runner_->PostTask( 451 FROM_HERE, 452 base::Bind(&DeletePortableDeviceOnBlockingPoolThread, 453 storage_device_info_.registered_device_path)); 454 while (!pending_tasks_.empty()) 455 pending_tasks_.pop(); 456 delete this; 457 } 458 459 void MTPDeviceDelegateImplWin::EnsureInitAndRunTask( 460 const PendingTaskInfo& task_info) { 461 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO)); 462 if ((init_state_ == INITIALIZED) && !task_in_progress_) { 463 DCHECK(pending_tasks_.empty()); 464 DCHECK(!current_snapshot_details_.get()); 465 base::PostTaskAndReplyWithResult(media_task_runner_, 466 task_info.location, 467 task_info.task, 468 task_info.reply); 469 task_in_progress_ = true; 470 return; 471 } 472 473 pending_tasks_.push(task_info); 474 if (init_state_ == UNINITIALIZED) { 475 init_state_ = PENDING_INIT; 476 base::PostTaskAndReplyWithResult( 477 media_task_runner_, 478 FROM_HERE, 479 base::Bind(&OpenDeviceOnBlockingPoolThread, 480 storage_device_info_.pnp_device_id, 481 storage_device_info_.registered_device_path), 482 base::Bind(&MTPDeviceDelegateImplWin::OnInitCompleted, 483 weak_ptr_factory_.GetWeakPtr())); 484 task_in_progress_ = true; 485 } 486 } 487 488 void MTPDeviceDelegateImplWin::WriteDataChunkIntoSnapshotFile() { 489 DCHECK(current_snapshot_details_.get()); 490 base::PostTaskAndReplyWithResult( 491 media_task_runner_, 492 FROM_HERE, 493 base::Bind(&WriteDataChunkIntoSnapshotFileOnBlockingPoolThread, 494 *current_snapshot_details_), 495 base::Bind(&MTPDeviceDelegateImplWin::OnWroteDataChunkIntoSnapshotFile, 496 weak_ptr_factory_.GetWeakPtr(), 497 current_snapshot_details_->request_info().snapshot_file_path)); 498 } 499 500 void MTPDeviceDelegateImplWin::ProcessNextPendingRequest() { 501 DCHECK(!task_in_progress_); 502 if (pending_tasks_.empty()) 503 return; 504 const PendingTaskInfo& task_info = pending_tasks_.front(); 505 task_in_progress_ = true; 506 base::PostTaskAndReplyWithResult(media_task_runner_, 507 task_info.location, 508 task_info.task, 509 task_info.reply); 510 pending_tasks_.pop(); 511 } 512 513 void MTPDeviceDelegateImplWin::OnInitCompleted(bool succeeded) { 514 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO)); 515 init_state_ = succeeded ? INITIALIZED : UNINITIALIZED; 516 task_in_progress_ = false; 517 ProcessNextPendingRequest(); 518 } 519 520 void MTPDeviceDelegateImplWin::OnGetFileInfo( 521 const GetFileInfoSuccessCallback& success_callback, 522 const ErrorCallback& error_callback, 523 base::PlatformFileInfo* file_info, 524 base::PlatformFileError error) { 525 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO)); 526 DCHECK(file_info); 527 if (error == base::PLATFORM_FILE_OK) 528 success_callback.Run(*file_info); 529 else 530 error_callback.Run(error); 531 task_in_progress_ = false; 532 ProcessNextPendingRequest(); 533 } 534 535 void MTPDeviceDelegateImplWin::OnDidReadDirectory( 536 const ReadDirectorySuccessCallback& success_callback, 537 const ErrorCallback& error_callback, 538 fileapi::AsyncFileUtil::EntryList* file_list, 539 base::PlatformFileError error) { 540 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO)); 541 DCHECK(file_list); 542 if (error == base::PLATFORM_FILE_OK) 543 success_callback.Run(*file_list, false /*no more entries*/); 544 else 545 error_callback.Run(error); 546 task_in_progress_ = false; 547 ProcessNextPendingRequest(); 548 } 549 550 void MTPDeviceDelegateImplWin::OnGetFileStream( 551 scoped_ptr<SnapshotFileDetails> file_details, 552 base::PlatformFileError error) { 553 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO)); 554 DCHECK(file_details); 555 DCHECK(!file_details->request_info().device_file_path.empty()); 556 DCHECK(!file_details->request_info().snapshot_file_path.empty()); 557 DCHECK(!current_snapshot_details_.get()); 558 if (error != base::PLATFORM_FILE_OK) { 559 file_details->request_info().error_callback.Run(error); 560 task_in_progress_ = false; 561 ProcessNextPendingRequest(); 562 return; 563 } 564 DCHECK(file_details->file_info().size == 0 || 565 file_details->device_file_stream()); 566 current_snapshot_details_.reset(file_details.release()); 567 WriteDataChunkIntoSnapshotFile(); 568 } 569 570 void MTPDeviceDelegateImplWin::OnWroteDataChunkIntoSnapshotFile( 571 const base::FilePath& snapshot_file_path, 572 DWORD bytes_written) { 573 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO)); 574 DCHECK(!snapshot_file_path.empty()); 575 if (!current_snapshot_details_.get()) 576 return; 577 DCHECK_EQ( 578 current_snapshot_details_->request_info().snapshot_file_path.value(), 579 snapshot_file_path.value()); 580 581 bool succeeded = false; 582 bool should_continue = false; 583 if (current_snapshot_details_->file_info().size > 0) { 584 if (current_snapshot_details_->AddBytesWritten(bytes_written)) { 585 if (current_snapshot_details_->IsSnapshotFileWriteComplete()) { 586 succeeded = true; 587 } else { 588 should_continue = true; 589 } 590 } 591 } else { 592 // Handle empty files. 593 DCHECK_EQ(0U, bytes_written); 594 succeeded = true; 595 } 596 597 if (should_continue) { 598 WriteDataChunkIntoSnapshotFile(); 599 return; 600 } 601 if (succeeded) { 602 current_snapshot_details_->request_info().success_callback.Run( 603 current_snapshot_details_->file_info(), 604 current_snapshot_details_->request_info().snapshot_file_path); 605 } else { 606 current_snapshot_details_->request_info().error_callback.Run( 607 base::PLATFORM_FILE_ERROR_FAILED); 608 } 609 task_in_progress_ = false; 610 current_snapshot_details_.reset(); 611 ProcessNextPendingRequest(); 612 } 613 614 } // namespace chrome 615