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 "components/storage_monitor/storage_monitor.h" 31 #include "content/public/browser/browser_thread.h" 32 #include "storage/common/fileapi/file_system_util.h" 33 34 namespace { 35 36 // Gets the details of the MTP partition storage specified by the 37 // |storage_path| on the UI thread. Returns true if the storage details are 38 // valid and returns false otherwise. 39 bool GetStorageInfoOnUIThread(const base::string16& storage_path, 40 base::string16* pnp_device_id, 41 base::string16* storage_object_id) { 42 DCHECK_CURRENTLY_ON(content::BrowserThread::UI); 43 DCHECK(!storage_path.empty()); 44 DCHECK(pnp_device_id); 45 DCHECK(storage_object_id); 46 base::string16 storage_device_id; 47 base::RemoveChars(storage_path, L"\\\\", &storage_device_id); 48 DCHECK(!storage_device_id.empty()); 49 // TODO(gbillock): Take the StorageMonitor as an argument. 50 storage_monitor::StorageMonitor* monitor = 51 storage_monitor::StorageMonitor::GetInstance(); 52 DCHECK(monitor); 53 return monitor->GetMTPStorageInfoFromDeviceId( 54 base::UTF16ToUTF8(storage_device_id), pnp_device_id, storage_object_id); 55 } 56 57 // Returns the object id of the file object specified by the |file_path|, 58 // e.g. if the |file_path| is "\\MTP:StorageSerial:SID-{1001,,192}:125\DCIM" 59 // and |device_info.registered_device_path_| is 60 // "\\MTP:StorageSerial:SID-{1001,,192}:125", this function returns the 61 // identifier of the "DCIM" folder object. 62 // 63 // Returns an empty string if the device is detached while the request is in 64 // progress or when the |file_path| is invalid. 65 base::string16 GetFileObjectIdFromPathOnBlockingPoolThread( 66 const MTPDeviceDelegateImplWin::StorageDeviceInfo& device_info, 67 const base::FilePath& file_path) { 68 base::ThreadRestrictions::AssertIOAllowed(); 69 DCHECK(!file_path.empty()); 70 IPortableDevice* device = 71 PortableDeviceMapService::GetInstance()->GetPortableDevice( 72 device_info.registered_device_path); 73 if (!device) 74 return base::string16(); 75 76 if (device_info.registered_device_path == file_path.value()) 77 return device_info.storage_object_id; 78 79 base::FilePath relative_path; 80 if (!base::FilePath(device_info.registered_device_path).AppendRelativePath( 81 file_path, &relative_path)) 82 return base::string16(); 83 84 std::vector<base::string16> path_components; 85 relative_path.GetComponents(&path_components); 86 DCHECK(!path_components.empty()); 87 base::string16 parent_id(device_info.storage_object_id); 88 base::string16 file_object_id; 89 for (size_t i = 0; i < path_components.size(); ++i) { 90 file_object_id = 91 media_transfer_protocol::GetObjectIdFromName(device, parent_id, 92 path_components[i]); 93 if (file_object_id.empty()) 94 break; 95 parent_id = file_object_id; 96 } 97 return file_object_id; 98 } 99 100 // Returns a pointer to a new instance of AbstractFileEnumerator for the given 101 // |root| directory. Called on a blocking pool thread. 102 scoped_ptr<MTPDeviceObjectEnumerator> 103 CreateFileEnumeratorOnBlockingPoolThread( 104 const MTPDeviceDelegateImplWin::StorageDeviceInfo& device_info, 105 const base::FilePath& root) { 106 base::ThreadRestrictions::AssertIOAllowed(); 107 DCHECK(!device_info.registered_device_path.empty()); 108 DCHECK(!root.empty()); 109 IPortableDevice* device = 110 PortableDeviceMapService::GetInstance()->GetPortableDevice( 111 device_info.registered_device_path); 112 if (!device) 113 return scoped_ptr<MTPDeviceObjectEnumerator>(); 114 115 base::string16 object_id = 116 GetFileObjectIdFromPathOnBlockingPoolThread(device_info, root); 117 if (object_id.empty()) 118 return scoped_ptr<MTPDeviceObjectEnumerator>(); 119 120 MTPDeviceObjectEntries entries; 121 if (!media_transfer_protocol::GetDirectoryEntries(device, object_id, 122 &entries) || 123 entries.empty()) 124 return scoped_ptr<MTPDeviceObjectEnumerator>(); 125 126 return scoped_ptr<MTPDeviceObjectEnumerator>( 127 new MTPDeviceObjectEnumerator(entries)); 128 } 129 130 // Opens the device for communication on a blocking pool thread. 131 // |pnp_device_id| specifies the PnP device id. 132 // |registered_device_path| specifies the registered file system root path for 133 // the given device. 134 bool OpenDeviceOnBlockingPoolThread( 135 const base::string16& pnp_device_id, 136 const base::string16& registered_device_path) { 137 base::ThreadRestrictions::AssertIOAllowed(); 138 DCHECK(!pnp_device_id.empty()); 139 DCHECK(!registered_device_path.empty()); 140 base::win::ScopedComPtr<IPortableDevice> device = 141 media_transfer_protocol::OpenDevice(pnp_device_id); 142 bool init_succeeded = device.get() != NULL; 143 if (init_succeeded) { 144 PortableDeviceMapService::GetInstance()->AddPortableDevice( 145 registered_device_path, device.get()); 146 } 147 return init_succeeded; 148 } 149 150 // Gets the |file_path| details from the MTP device specified by the 151 // |device_info.registered_device_path|. On success, |error| is set to 152 // base::File::FILE_OK and fills in |file_info|. On failure, |error| is set 153 // to corresponding platform file error and |file_info| is not set. 154 base::File::Error GetFileInfoOnBlockingPoolThread( 155 const MTPDeviceDelegateImplWin::StorageDeviceInfo& device_info, 156 const base::FilePath& file_path, 157 base::File::Info* file_info) { 158 base::ThreadRestrictions::AssertIOAllowed(); 159 DCHECK(!device_info.registered_device_path.empty()); 160 DCHECK(!file_path.empty()); 161 DCHECK(file_info); 162 IPortableDevice* device = 163 PortableDeviceMapService::GetInstance()->GetPortableDevice( 164 device_info.registered_device_path); 165 if (!device) 166 return base::File::FILE_ERROR_FAILED; 167 168 base::string16 object_id = 169 GetFileObjectIdFromPathOnBlockingPoolThread(device_info, file_path); 170 if (object_id.empty()) 171 return base::File::FILE_ERROR_FAILED; 172 return media_transfer_protocol::GetFileEntryInfo(device, object_id, 173 file_info); 174 } 175 176 // Reads the |root| directory file entries on a blocking pool thread. On 177 // success, |error| is set to base::File::FILE_OK and |entries| contains the 178 // directory file entries. On failure, |error| is set to platform file error 179 // and |entries| is not set. 180 base::File::Error ReadDirectoryOnBlockingPoolThread( 181 const MTPDeviceDelegateImplWin::StorageDeviceInfo& device_info, 182 const base::FilePath& root, 183 storage::AsyncFileUtil::EntryList* entries) { 184 base::ThreadRestrictions::AssertIOAllowed(); 185 DCHECK(!root.empty()); 186 DCHECK(entries); 187 base::File::Info file_info; 188 base::File::Error error = GetFileInfoOnBlockingPoolThread(device_info, root, 189 &file_info); 190 if (error != base::File::FILE_OK) 191 return error; 192 193 if (!file_info.is_directory) 194 return base::File::FILE_ERROR_NOT_A_DIRECTORY; 195 196 base::FilePath current; 197 scoped_ptr<MTPDeviceObjectEnumerator> file_enum = 198 CreateFileEnumeratorOnBlockingPoolThread(device_info, root); 199 if (!file_enum) 200 return error; 201 202 while (!(current = file_enum->Next()).empty()) { 203 storage::DirectoryEntry entry; 204 entry.is_directory = file_enum->IsDirectory(); 205 entry.name = storage::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::File::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::File::Error 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::File::FILE_ERROR_FAILED; 230 231 base::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::File::FILE_ERROR_FAILED; 236 237 base::File::Info file_info; 238 base::File::Error error = 239 GetFileInfoOnBlockingPoolThread( 240 device_info, 241 file_details->request_info().device_file_path, 242 &file_info); 243 if (error != base::File::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::File::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 base::string16& registered_device_path) { 291 base::ThreadRestrictions::AssertIOAllowed(); 292 PortableDeviceMapService::GetInstance()->RemovePortableDevice( 293 registered_device_path); 294 } 295 296 } // namespace 297 298 // Used by CreateMTPDeviceAsyncDelegate() to create the MTP device 299 // delegate on the IO thread. 300 void OnGetStorageInfoCreateDelegate( 301 const base::string16& device_location, 302 const CreateMTPDeviceAsyncDelegateCallback& callback, 303 base::string16* pnp_device_id, 304 base::string16* storage_object_id, 305 bool succeeded) { 306 DCHECK_CURRENTLY_ON(content::BrowserThread::IO); 307 DCHECK(pnp_device_id); 308 DCHECK(storage_object_id); 309 if (!succeeded) 310 return; 311 callback.Run(new MTPDeviceDelegateImplWin(device_location, 312 *pnp_device_id, 313 *storage_object_id)); 314 } 315 316 void CreateMTPDeviceAsyncDelegate( 317 const base::string16& device_location, 318 const CreateMTPDeviceAsyncDelegateCallback& callback) { 319 DCHECK_CURRENTLY_ON(content::BrowserThread::IO); 320 DCHECK(!device_location.empty()); 321 base::string16* pnp_device_id = new base::string16; 322 base::string16* storage_object_id = new base::string16; 323 content::BrowserThread::PostTaskAndReplyWithResult<bool>( 324 content::BrowserThread::UI, 325 FROM_HERE, 326 base::Bind(&GetStorageInfoOnUIThread, 327 device_location, 328 base::Unretained(pnp_device_id), 329 base::Unretained(storage_object_id)), 330 base::Bind(&OnGetStorageInfoCreateDelegate, 331 device_location, 332 callback, 333 base::Owned(pnp_device_id), 334 base::Owned(storage_object_id))); 335 } 336 337 // MTPDeviceDelegateImplWin --------------------------------------------------- 338 339 MTPDeviceDelegateImplWin::StorageDeviceInfo::StorageDeviceInfo( 340 const base::string16& pnp_device_id, 341 const base::string16& registered_device_path, 342 const base::string16& storage_object_id) 343 : pnp_device_id(pnp_device_id), 344 registered_device_path(registered_device_path), 345 storage_object_id(storage_object_id) { 346 DCHECK_CURRENTLY_ON(content::BrowserThread::IO); 347 } 348 349 MTPDeviceDelegateImplWin::PendingTaskInfo::PendingTaskInfo( 350 const tracked_objects::Location& location, 351 const base::Callback<base::File::Error(void)>& task, 352 const base::Callback<void(base::File::Error)>& reply) 353 : location(location), 354 task(task), 355 reply(reply) { 356 } 357 358 MTPDeviceDelegateImplWin::MTPDeviceDelegateImplWin( 359 const base::string16& registered_device_path, 360 const base::string16& pnp_device_id, 361 const base::string16& storage_object_id) 362 : storage_device_info_(pnp_device_id, registered_device_path, 363 storage_object_id), 364 init_state_(UNINITIALIZED), 365 media_task_runner_(MediaFileSystemBackend::MediaTaskRunner()), 366 task_in_progress_(false), 367 weak_ptr_factory_(this) { 368 DCHECK_CURRENTLY_ON(content::BrowserThread::IO); 369 DCHECK(!registered_device_path.empty()); 370 DCHECK(!pnp_device_id.empty()); 371 DCHECK(!storage_object_id.empty()); 372 DCHECK(media_task_runner_.get()); 373 } 374 375 MTPDeviceDelegateImplWin::~MTPDeviceDelegateImplWin() { 376 DCHECK_CURRENTLY_ON(content::BrowserThread::IO); 377 } 378 379 void MTPDeviceDelegateImplWin::GetFileInfo( 380 const base::FilePath& file_path, 381 const GetFileInfoSuccessCallback& success_callback, 382 const ErrorCallback& error_callback) { 383 DCHECK_CURRENTLY_ON(content::BrowserThread::IO); 384 DCHECK(!file_path.empty()); 385 base::File::Info* file_info = new base::File::Info; 386 EnsureInitAndRunTask( 387 PendingTaskInfo(FROM_HERE, 388 base::Bind(&GetFileInfoOnBlockingPoolThread, 389 storage_device_info_, 390 file_path, 391 base::Unretained(file_info)), 392 base::Bind(&MTPDeviceDelegateImplWin::OnGetFileInfo, 393 weak_ptr_factory_.GetWeakPtr(), 394 success_callback, 395 error_callback, 396 base::Owned(file_info)))); 397 } 398 399 void MTPDeviceDelegateImplWin::ReadDirectory( 400 const base::FilePath& root, 401 const ReadDirectorySuccessCallback& success_callback, 402 const ErrorCallback& error_callback) { 403 DCHECK_CURRENTLY_ON(content::BrowserThread::IO); 404 DCHECK(!root.empty()); 405 storage::AsyncFileUtil::EntryList* entries = 406 new storage::AsyncFileUtil::EntryList; 407 EnsureInitAndRunTask( 408 PendingTaskInfo(FROM_HERE, 409 base::Bind(&ReadDirectoryOnBlockingPoolThread, 410 storage_device_info_, 411 root, 412 base::Unretained(entries)), 413 base::Bind(&MTPDeviceDelegateImplWin::OnDidReadDirectory, 414 weak_ptr_factory_.GetWeakPtr(), 415 success_callback, 416 error_callback, 417 base::Owned(entries)))); 418 } 419 420 void MTPDeviceDelegateImplWin::CreateSnapshotFile( 421 const base::FilePath& device_file_path, 422 const base::FilePath& snapshot_file_path, 423 const CreateSnapshotFileSuccessCallback& success_callback, 424 const ErrorCallback& error_callback) { 425 DCHECK_CURRENTLY_ON(content::BrowserThread::IO); 426 DCHECK(!device_file_path.empty()); 427 DCHECK(!snapshot_file_path.empty()); 428 scoped_ptr<SnapshotFileDetails> file_details( 429 new SnapshotFileDetails(SnapshotRequestInfo(device_file_path, 430 snapshot_file_path, 431 success_callback, 432 error_callback))); 433 // Passing a raw SnapshotFileDetails* to the blocking pool is safe, because 434 // it is owned by |file_details| in the reply callback. 435 EnsureInitAndRunTask( 436 PendingTaskInfo(FROM_HERE, 437 base::Bind(&GetFileStreamOnBlockingPoolThread, 438 storage_device_info_, 439 file_details.get()), 440 base::Bind(&MTPDeviceDelegateImplWin::OnGetFileStream, 441 weak_ptr_factory_.GetWeakPtr(), 442 base::Passed(&file_details)))); 443 } 444 445 bool MTPDeviceDelegateImplWin::IsStreaming() { 446 return false; 447 } 448 449 void MTPDeviceDelegateImplWin::ReadBytes( 450 const base::FilePath& device_file_path, 451 const scoped_refptr<net::IOBuffer>& buf, 452 int64 offset, 453 int buf_len, 454 const ReadBytesSuccessCallback& success_callback, 455 const ErrorCallback& error_callback) { 456 NOTREACHED(); 457 } 458 459 void MTPDeviceDelegateImplWin::CancelPendingTasksAndDeleteDelegate() { 460 DCHECK_CURRENTLY_ON(content::BrowserThread::IO); 461 PortableDeviceMapService::GetInstance()->MarkPortableDeviceForDeletion( 462 storage_device_info_.registered_device_path); 463 media_task_runner_->PostTask( 464 FROM_HERE, 465 base::Bind(&DeletePortableDeviceOnBlockingPoolThread, 466 storage_device_info_.registered_device_path)); 467 while (!pending_tasks_.empty()) 468 pending_tasks_.pop(); 469 delete this; 470 } 471 472 void MTPDeviceDelegateImplWin::EnsureInitAndRunTask( 473 const PendingTaskInfo& task_info) { 474 DCHECK_CURRENTLY_ON(content::BrowserThread::IO); 475 if ((init_state_ == INITIALIZED) && !task_in_progress_) { 476 DCHECK(pending_tasks_.empty()); 477 DCHECK(!current_snapshot_details_.get()); 478 base::PostTaskAndReplyWithResult(media_task_runner_, 479 task_info.location, 480 task_info.task, 481 task_info.reply); 482 task_in_progress_ = true; 483 return; 484 } 485 486 pending_tasks_.push(task_info); 487 if (init_state_ == UNINITIALIZED) { 488 init_state_ = PENDING_INIT; 489 base::PostTaskAndReplyWithResult( 490 media_task_runner_, 491 FROM_HERE, 492 base::Bind(&OpenDeviceOnBlockingPoolThread, 493 storage_device_info_.pnp_device_id, 494 storage_device_info_.registered_device_path), 495 base::Bind(&MTPDeviceDelegateImplWin::OnInitCompleted, 496 weak_ptr_factory_.GetWeakPtr())); 497 task_in_progress_ = true; 498 } 499 } 500 501 void MTPDeviceDelegateImplWin::WriteDataChunkIntoSnapshotFile() { 502 DCHECK(current_snapshot_details_.get()); 503 base::PostTaskAndReplyWithResult( 504 media_task_runner_, 505 FROM_HERE, 506 base::Bind(&WriteDataChunkIntoSnapshotFileOnBlockingPoolThread, 507 *current_snapshot_details_), 508 base::Bind(&MTPDeviceDelegateImplWin::OnWroteDataChunkIntoSnapshotFile, 509 weak_ptr_factory_.GetWeakPtr(), 510 current_snapshot_details_->request_info().snapshot_file_path)); 511 } 512 513 void MTPDeviceDelegateImplWin::ProcessNextPendingRequest() { 514 DCHECK(!task_in_progress_); 515 if (pending_tasks_.empty()) 516 return; 517 const PendingTaskInfo& task_info = pending_tasks_.front(); 518 task_in_progress_ = true; 519 base::PostTaskAndReplyWithResult(media_task_runner_, 520 task_info.location, 521 task_info.task, 522 task_info.reply); 523 pending_tasks_.pop(); 524 } 525 526 void MTPDeviceDelegateImplWin::OnInitCompleted(bool succeeded) { 527 DCHECK_CURRENTLY_ON(content::BrowserThread::IO); 528 init_state_ = succeeded ? INITIALIZED : UNINITIALIZED; 529 task_in_progress_ = false; 530 ProcessNextPendingRequest(); 531 } 532 533 void MTPDeviceDelegateImplWin::OnGetFileInfo( 534 const GetFileInfoSuccessCallback& success_callback, 535 const ErrorCallback& error_callback, 536 base::File::Info* file_info, 537 base::File::Error error) { 538 DCHECK_CURRENTLY_ON(content::BrowserThread::IO); 539 DCHECK(file_info); 540 if (error == base::File::FILE_OK) 541 success_callback.Run(*file_info); 542 else 543 error_callback.Run(error); 544 task_in_progress_ = false; 545 ProcessNextPendingRequest(); 546 } 547 548 void MTPDeviceDelegateImplWin::OnDidReadDirectory( 549 const ReadDirectorySuccessCallback& success_callback, 550 const ErrorCallback& error_callback, 551 storage::AsyncFileUtil::EntryList* file_list, 552 base::File::Error error) { 553 DCHECK_CURRENTLY_ON(content::BrowserThread::IO); 554 DCHECK(file_list); 555 if (error == base::File::FILE_OK) 556 success_callback.Run(*file_list, false /*no more entries*/); 557 else 558 error_callback.Run(error); 559 task_in_progress_ = false; 560 ProcessNextPendingRequest(); 561 } 562 563 void MTPDeviceDelegateImplWin::OnGetFileStream( 564 scoped_ptr<SnapshotFileDetails> file_details, 565 base::File::Error error) { 566 DCHECK_CURRENTLY_ON(content::BrowserThread::IO); 567 DCHECK(file_details); 568 DCHECK(!file_details->request_info().device_file_path.empty()); 569 DCHECK(!file_details->request_info().snapshot_file_path.empty()); 570 DCHECK(!current_snapshot_details_.get()); 571 if (error != base::File::FILE_OK) { 572 file_details->request_info().error_callback.Run(error); 573 task_in_progress_ = false; 574 ProcessNextPendingRequest(); 575 return; 576 } 577 DCHECK(file_details->file_info().size == 0 || 578 file_details->device_file_stream()); 579 current_snapshot_details_.reset(file_details.release()); 580 WriteDataChunkIntoSnapshotFile(); 581 } 582 583 void MTPDeviceDelegateImplWin::OnWroteDataChunkIntoSnapshotFile( 584 const base::FilePath& snapshot_file_path, 585 DWORD bytes_written) { 586 DCHECK_CURRENTLY_ON(content::BrowserThread::IO); 587 DCHECK(!snapshot_file_path.empty()); 588 if (!current_snapshot_details_.get()) 589 return; 590 DCHECK_EQ( 591 current_snapshot_details_->request_info().snapshot_file_path.value(), 592 snapshot_file_path.value()); 593 594 bool succeeded = false; 595 bool should_continue = false; 596 if (current_snapshot_details_->file_info().size > 0) { 597 if (current_snapshot_details_->AddBytesWritten(bytes_written)) { 598 if (current_snapshot_details_->IsSnapshotFileWriteComplete()) { 599 succeeded = true; 600 } else { 601 should_continue = true; 602 } 603 } 604 } else { 605 // Handle empty files. 606 DCHECK_EQ(0U, bytes_written); 607 succeeded = true; 608 } 609 610 if (should_continue) { 611 WriteDataChunkIntoSnapshotFile(); 612 return; 613 } 614 if (succeeded) { 615 current_snapshot_details_->request_info().success_callback.Run( 616 current_snapshot_details_->file_info(), 617 current_snapshot_details_->request_info().snapshot_file_path); 618 } else { 619 current_snapshot_details_->request_info().error_callback.Run( 620 base::File::FILE_ERROR_FAILED); 621 } 622 task_in_progress_ = false; 623 current_snapshot_details_.reset(); 624 ProcessNextPendingRequest(); 625 } 626