OLD | NEW |
---|---|
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "chrome/browser/media_gallery/mtp_device_delegate_impl_linux.h" | 5 #include "chrome/browser/media_gallery/mtp_device_delegate_impl_linux.h" |
6 | 6 |
7 #include <fcntl.h> | |
8 #include <sys/stat.h> | |
9 #include <sys/types.h> | |
10 | |
7 #include "base/bind.h" | 11 #include "base/bind.h" |
8 #include "base/file_path.h" | 12 #include "base/file_path.h" |
9 #include "base/file_util.h" | 13 #include "base/file_util.h" |
10 #include "base/sequenced_task_runner.h" | 14 #include "base/sequenced_task_runner.h" |
11 #include "base/sequenced_task_runner_helpers.h" | 15 #include "base/sequenced_task_runner_helpers.h" |
12 #include "base/string_util.h" | 16 #include "base/string_util.h" |
13 #include "base/synchronization/cancellation_flag.h" | 17 #include "base/synchronization/cancellation_flag.h" |
14 #include "base/threading/sequenced_worker_pool.h" | 18 #include "base/threading/sequenced_worker_pool.h" |
15 #include "chrome/browser/media_transfer_protocol/media_transfer_protocol_manager .h" | 19 #include "chrome/browser/media_transfer_protocol/media_transfer_protocol_manager .h" |
16 #include "chrome/browser/media_transfer_protocol/mtp_file_entry.pb.h" | 20 #include "chrome/browser/media_transfer_protocol/mtp_file_entry.pb.h" |
(...skipping 328 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
345 | 349 |
346 DISALLOW_COPY_AND_ASSIGN(GetFileInfoWorker); | 350 DISALLOW_COPY_AND_ASSIGN(GetFileInfoWorker); |
347 }; | 351 }; |
348 | 352 |
349 // Worker class to read media device file data given a file |path|. | 353 // Worker class to read media device file data given a file |path|. |
350 class ReadFileWorker | 354 class ReadFileWorker |
351 : public RefCountedThreadSafe<ReadFileWorker, ReadFileWorkerDeleter> { | 355 : public RefCountedThreadSafe<ReadFileWorker, ReadFileWorkerDeleter> { |
352 public: | 356 public: |
353 // Constructed on |media_task_runner_| thread. | 357 // Constructed on |media_task_runner_| thread. |
354 ReadFileWorker(const std::string& handle, | 358 ReadFileWorker(const std::string& handle, |
355 const std::string& path, | 359 const std::string& src_path, |
356 uint32 total_size, | 360 uint32 total_size, |
361 const FilePath& dest_path, | |
357 SequencedTaskRunner* task_runner, | 362 SequencedTaskRunner* task_runner, |
358 WaitableEvent* task_completed_event, | 363 WaitableEvent* task_completed_event, |
359 WaitableEvent* shutdown_event) | 364 WaitableEvent* shutdown_event) |
360 : device_handle_(handle), | 365 : device_handle_(handle), |
361 path_(path), | 366 src_path_(src_path), |
367 bytes_read_(0), | |
362 total_bytes_(total_size), | 368 total_bytes_(total_size), |
369 dest_path_(dest_path), | |
370 dest_fd_(-1), | |
363 error_occurred_(false), | 371 error_occurred_(false), |
364 media_task_runner_(task_runner), | 372 media_task_runner_(task_runner), |
365 on_task_completed_event_(task_completed_event), | 373 on_task_completed_event_(task_completed_event), |
366 on_shutdown_event_(shutdown_event) { | 374 on_shutdown_event_(shutdown_event) { |
367 DCHECK(on_task_completed_event_); | 375 DCHECK(on_task_completed_event_); |
368 DCHECK(on_shutdown_event_); | 376 DCHECK(on_shutdown_event_); |
369 } | 377 } |
370 | 378 |
371 // This function is invoked on |media_task_runner_| to post the task on UI | 379 // This function is invoked on |media_task_runner_| to post the task on UI |
372 // thread. This blocks the |media_task_runner_| until the task is complete. | 380 // thread. This blocks the |media_task_runner_| until the task is complete. |
373 void Run() { | 381 void Run() { |
374 if (on_shutdown_event_->IsSignaled()) { | 382 if (on_shutdown_event_->IsSignaled()) { |
375 // Process is in shutdown mode. | 383 // Process is in shutdown mode. |
376 // Do not post any task on |media_task_runner_|. | 384 // Do not post any task on |media_task_runner_|. |
377 return; | 385 return; |
378 } | 386 } |
379 | 387 |
380 while (!error_occurred_ && (data_.size() < total_bytes_) && | 388 dest_fd_ = open(dest_path_.value().c_str(), O_WRONLY); |
381 !cancel_tasks_flag_.IsSet()) { | 389 if (dest_fd_ < 0) |
390 return; | |
391 dest_fd_scoper_.reset(&dest_fd_); | |
392 | |
393 while (!error_occurred_ && (bytes_read_ < total_bytes_)) { | |
382 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, | 394 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, |
383 Bind(&ReadFileWorker::DoWorkOnUIThread, this)); | 395 Bind(&ReadFileWorker::DoWorkOnUIThread, this)); |
384 on_task_completed_event_->Wait(); | 396 on_task_completed_event_->Wait(); |
385 if (on_shutdown_event_->IsSignaled()) | 397 if (on_shutdown_event_->IsSignaled()) { |
386 cancel_tasks_flag_.Set(); | 398 cancel_tasks_flag_.Set(); |
399 break; | |
400 } | |
387 } | 401 } |
388 } | 402 } |
389 | 403 |
390 // Returns the media file contents received from mtpd. | 404 bool Succeeded() const { |
391 const std::string& data() const { return data_; } | 405 return !error_occurred_ && bytes_read_ == total_bytes_; |
406 } | |
392 | 407 |
393 // Returns the |media_task_runner_| associated with this worker object. | 408 // Returns the |media_task_runner_| associated with this worker object. |
394 // This function is exposed for WorkerDeleter struct to access the | 409 // This function is exposed for WorkerDeleter struct to access the |
395 // |media_task_runner_|. | 410 // |media_task_runner_|. |
396 SequencedTaskRunner* media_task_runner() const { | 411 SequencedTaskRunner* media_task_runner() const { |
397 return media_task_runner_.get(); | 412 return media_task_runner_.get(); |
398 } | 413 } |
399 | 414 |
400 private: | 415 private: |
401 friend struct WorkerDeleter<ReadFileWorker>; | 416 friend struct WorkerDeleter<ReadFileWorker>; |
402 friend class DeleteHelper<ReadFileWorker>; | 417 friend class DeleteHelper<ReadFileWorker>; |
403 friend class RefCountedThreadSafe<ReadFileWorker, ReadFileWorkerDeleter>; | 418 friend class RefCountedThreadSafe<ReadFileWorker, ReadFileWorkerDeleter>; |
404 | 419 |
405 // Destructed via ReadFileWorkerDeleter. | 420 // Destructed via ReadFileWorkerDeleter. |
406 virtual ~ReadFileWorker() { | 421 virtual ~ReadFileWorker() { |
407 // This object must be destructed on |media_task_runner_|. | 422 // This object must be destructed on |media_task_runner_|. |
408 } | 423 } |
409 | 424 |
410 // Dispatches a request to MediaTransferProtocolManager to get the media file | 425 // Dispatches a request to MediaTransferProtocolManager to get the media file |
411 // contents. | 426 // contents. |
412 void DoWorkOnUIThread() { | 427 void DoWorkOnUIThread() { |
413 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | 428 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
414 if (cancel_tasks_flag_.IsSet()) | 429 if (cancel_tasks_flag_.IsSet()) |
415 return; | 430 return; |
416 | 431 |
417 GetMediaTransferProtocolManager()->ReadFileChunkByPath( | 432 GetMediaTransferProtocolManager()->ReadFileChunkByPath( |
418 device_handle_, path_, data_.size(), BytesToRead(), | 433 device_handle_, src_path_, bytes_read_, BytesToRead(), |
419 Bind(&ReadFileWorker::OnDidWorkOnUIThread, this)); | 434 Bind(&ReadFileWorker::OnDidWorkOnUIThread, this)); |
420 } | 435 } |
421 | 436 |
422 // Query callback for DoWorkOnUIThread(). On success, |data| has the media | 437 // Query callback for DoWorkOnUIThread(). On success, |data| has the media |
423 // file contents. On failure, |error| is set to true. This function signals | 438 // file contents. On failure, |error| is set to true. This function signals |
424 // to unblock |media_task_runner_|. | 439 // to unblock |media_task_runner_|. |
425 void OnDidWorkOnUIThread(const std::string& data, bool error) { | 440 void OnDidWorkOnUIThread(const std::string& data, bool error) { |
426 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | 441 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
427 if (cancel_tasks_flag_.IsSet()) | 442 if (cancel_tasks_flag_.IsSet()) |
428 return; | 443 return; |
429 | 444 |
430 error_occurred_ = error; | 445 error_occurred_ = error || (data.size() != BytesToRead()); |
431 if (!error) { | 446 if (!error_occurred_) { |
432 if ((BytesToRead() == data.size())) { | 447 int bytes_written = |
433 // TODO(kmadhusu): Data could be really huge. Consider passing data by | 448 file_util::WriteFileDescriptor(dest_fd_, data.data(), data.size()); |
kmadhusu
2012/11/30 01:13:09
You are trying to do a file write operation on a U
| |
434 // pointer/ref rather than by value here to avoid an extra data copy. | 449 if (static_cast<int>(data.size()) == bytes_written) |
435 data_.append(data); | 450 bytes_read_ += data.size(); |
436 } else { | 451 else |
437 NOTREACHED(); | |
438 error_occurred_ = true; | 452 error_occurred_ = true; |
439 } | |
440 } | 453 } |
441 on_task_completed_event_->Signal(); | 454 on_task_completed_event_->Signal(); |
442 } | 455 } |
443 | 456 |
444 uint32 BytesToRead() const { | 457 uint32 BytesToRead() const { |
445 // Read data in 1 MB chunks. | 458 // Read data in 1 MB chunks. |
446 static const uint32 kReadChunkSize = 1024 * 1024; | 459 static const uint32 kReadChunkSize = 1024 * 1024; |
447 return std::min(kReadChunkSize, | 460 return std::min(kReadChunkSize, total_bytes_ - bytes_read_); |
448 total_bytes_ - static_cast<uint32>(data_.size())); | |
449 } | 461 } |
450 | 462 |
451 // The device unique identifier to query the device. | 463 // The device unique identifier to query the device. |
452 const std::string device_handle_; | 464 const std::string device_handle_; |
453 | 465 |
454 // The media device file path. | 466 // The media device file path. |
455 const std::string path_; | 467 const std::string src_path_; |
456 | 468 |
457 // The data from mtpd. | 469 // Number of bytes read from the device. |
458 std::string data_; | 470 uint32 bytes_read_; |
459 | 471 |
460 // Number of bytes to read. | 472 // Number of bytes to read. |
461 const uint32 total_bytes_; | 473 const uint32 total_bytes_; |
462 | 474 |
475 // Where to write the data read from the device. | |
476 const FilePath dest_path_; | |
477 | |
478 // File descriptor for |dest_path_|. | |
479 int dest_fd_; | |
480 | |
481 // Scoper to cleanup |dest_fd_|. | |
482 file_util::ScopedFD dest_fd_scoper_; | |
483 | |
463 // Whether an error occurred during file transfer. | 484 // Whether an error occurred during file transfer. |
464 bool error_occurred_; | 485 bool error_occurred_; |
465 | 486 |
466 // A reference to |media_task_runner_| to destruct this object on the correct | 487 // A reference to |media_task_runner_| to destruct this object on the correct |
467 // thread. | 488 // thread. |
468 scoped_refptr<SequencedTaskRunner> media_task_runner_; | 489 scoped_refptr<SequencedTaskRunner> media_task_runner_; |
469 | 490 |
470 // |media_task_runner_| can wait on this event until the required operation | 491 // |media_task_runner_| can wait on this event until the required operation |
471 // is complete. | 492 // is complete. |
472 // TODO(kmadhusu): Remove this WaitableEvent after modifying the | 493 // TODO(kmadhusu): Remove this WaitableEvent after modifying the |
(...skipping 380 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
853 PlatformFileError error = GetFileInfo(device_file_path, file_info); | 874 PlatformFileError error = GetFileInfo(device_file_path, file_info); |
854 if (error != base::PLATFORM_FILE_OK) | 875 if (error != base::PLATFORM_FILE_OK) |
855 return error; | 876 return error; |
856 | 877 |
857 if (file_info->size <= 0 || file_info->size > kuint32max) | 878 if (file_info->size <= 0 || file_info->size > kuint32max) |
858 return base::PLATFORM_FILE_ERROR_FAILED; | 879 return base::PLATFORM_FILE_ERROR_FAILED; |
859 | 880 |
860 scoped_refptr<ReadFileWorker> worker(new ReadFileWorker( | 881 scoped_refptr<ReadFileWorker> worker(new ReadFileWorker( |
861 device_handle_, | 882 device_handle_, |
862 GetDeviceRelativePath(device_path_, device_file_path.value()), | 883 GetDeviceRelativePath(device_path_, device_file_path.value()), |
863 file_info->size, | 884 file_info->size, local_path, |
864 media_task_runner_, &on_task_completed_event_, &on_shutdown_event_)); | 885 media_task_runner_, &on_task_completed_event_, &on_shutdown_event_)); |
865 worker->Run(); | 886 worker->Run(); |
866 | 887 |
867 const std::string& file_data = worker->data(); | 888 if (!worker->Succeeded()) |
868 int data_size = static_cast<int>(file_data.length()); | |
869 if (file_data.empty() || | |
870 file_util::WriteFile(local_path, file_data.c_str(), | |
871 data_size) != data_size) { | |
872 return base::PLATFORM_FILE_ERROR_FAILED; | 889 return base::PLATFORM_FILE_ERROR_FAILED; |
873 } | |
874 | 890 |
875 // Modify the last modified time to null. This prevents the time stamp | 891 // Modify the last modified time to null. This prevents the time stamp |
876 // verfication in LocalFileStreamReader. | 892 // verfication in LocalFileStreamReader. |
877 file_info->last_modified = base::Time(); | 893 file_info->last_modified = base::Time(); |
878 return error; | 894 return base::PLATFORM_FILE_OK; |
879 } | 895 } |
880 | 896 |
881 SequencedTaskRunner* MTPDeviceDelegateImplLinux::GetMediaTaskRunner() { | 897 SequencedTaskRunner* MTPDeviceDelegateImplLinux::GetMediaTaskRunner() { |
882 return media_task_runner_.get(); | 898 return media_task_runner_.get(); |
883 } | 899 } |
884 | 900 |
885 void MTPDeviceDelegateImplLinux::CancelPendingTasksAndDeleteDelegate() { | 901 void MTPDeviceDelegateImplLinux::CancelPendingTasksAndDeleteDelegate() { |
886 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); | 902 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); |
887 // Caution: This function is called on the IO thread. Access only the thread | 903 // Caution: This function is called on the IO thread. Access only the thread |
888 // safe member variables in this function. Do all the clean up operations in | 904 // safe member variables in this function. Do all the clean up operations in |
(...skipping 40 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
929 } | 945 } |
930 | 946 |
931 void MTPDeviceDelegateImplLinux::DeleteDelegateOnTaskRunner() { | 947 void MTPDeviceDelegateImplLinux::DeleteDelegateOnTaskRunner() { |
932 DCHECK(media_task_runner_->RunsTasksOnCurrentThread()); | 948 DCHECK(media_task_runner_->RunsTasksOnCurrentThread()); |
933 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, | 949 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, |
934 Bind(&CloseStorageOnUIThread, device_handle_)); | 950 Bind(&CloseStorageOnUIThread, device_handle_)); |
935 delete this; | 951 delete this; |
936 } | 952 } |
937 | 953 |
938 } // namespace chrome | 954 } // namespace chrome |
OLD | NEW |