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 "chrome/browser/media_galleries/linux/mtp_device_delegate_impl_linux.h" 6 7#include <algorithm> 8#include <vector> 9 10#include "base/bind.h" 11#include "base/numerics/safe_conversions.h" 12#include "base/strings/string_number_conversions.h" 13#include "base/strings/string_split.h" 14#include "base/strings/string_util.h" 15#include "chrome/browser/media_galleries/linux/mtp_device_task_helper.h" 16#include "chrome/browser/media_galleries/linux/mtp_device_task_helper_map_service.h" 17#include "chrome/browser/media_galleries/linux/snapshot_file_details.h" 18#include "net/base/io_buffer.h" 19#include "third_party/cros_system_api/dbus/service_constants.h" 20 21namespace { 22 23// File path separator constant. 24const char kRootPath[] = "/"; 25 26// Returns the device relative file path given |file_path|. 27// E.g.: If the |file_path| is "/usb:2,2:12345/DCIM" and |registered_dev_path| 28// is "/usb:2,2:12345", this function returns the device relative path which is 29// "DCIM". 30// In the special case when |registered_dev_path| and |file_path| are the same, 31// return |kRootPath|. 32std::string GetDeviceRelativePath(const base::FilePath& registered_dev_path, 33 const base::FilePath& file_path) { 34 DCHECK_CURRENTLY_ON(content::BrowserThread::IO); 35 DCHECK(!registered_dev_path.empty()); 36 DCHECK(!file_path.empty()); 37 std::string result; 38 if (registered_dev_path == file_path) { 39 result = kRootPath; 40 } else { 41 base::FilePath relative_path; 42 if (registered_dev_path.AppendRelativePath(file_path, &relative_path)) { 43 DCHECK(!relative_path.empty()); 44 result = relative_path.value(); 45 } 46 } 47 return result; 48} 49 50// Returns the MTPDeviceTaskHelper object associated with the MTP device 51// storage. 52// 53// |storage_name| specifies the name of the storage device. 54// Returns NULL if the |storage_name| is no longer valid (e.g. because the 55// corresponding storage device is detached, etc). 56MTPDeviceTaskHelper* GetDeviceTaskHelperForStorage( 57 const std::string& storage_name) { 58 DCHECK_CURRENTLY_ON(content::BrowserThread::UI); 59 return MTPDeviceTaskHelperMapService::GetInstance()->GetDeviceTaskHelper( 60 storage_name); 61} 62 63// Opens the storage device for communication. 64// 65// Called on the UI thread to dispatch the request to the 66// MediaTransferProtocolManager. 67// 68// |storage_name| specifies the name of the storage device. 69// |reply_callback| is called when the OpenStorage request completes. 70// |reply_callback| runs on the IO thread. 71void OpenStorageOnUIThread( 72 const std::string& storage_name, 73 const MTPDeviceTaskHelper::OpenStorageCallback& reply_callback) { 74 DCHECK_CURRENTLY_ON(content::BrowserThread::UI); 75 MTPDeviceTaskHelper* task_helper = 76 GetDeviceTaskHelperForStorage(storage_name); 77 if (!task_helper) { 78 task_helper = 79 MTPDeviceTaskHelperMapService::GetInstance()->CreateDeviceTaskHelper( 80 storage_name); 81 } 82 task_helper->OpenStorage(storage_name, reply_callback); 83} 84 85// Enumerates the |dir_id| directory file entries. 86// 87// Called on the UI thread to dispatch the request to the 88// MediaTransferProtocolManager. 89// 90// |storage_name| specifies the name of the storage device. 91// |success_callback| is called when the ReadDirectory request succeeds. 92// |error_callback| is called when the ReadDirectory request fails. 93// |success_callback| and |error_callback| runs on the IO thread. 94void ReadDirectoryOnUIThread( 95 const std::string& storage_name, 96 uint32 dir_id, 97 const MTPDeviceTaskHelper::ReadDirectorySuccessCallback& success_callback, 98 const MTPDeviceTaskHelper::ErrorCallback& error_callback) { 99 DCHECK_CURRENTLY_ON(content::BrowserThread::UI); 100 MTPDeviceTaskHelper* task_helper = 101 GetDeviceTaskHelperForStorage(storage_name); 102 if (!task_helper) 103 return; 104 task_helper->ReadDirectory(dir_id, success_callback, error_callback); 105} 106 107// Gets the |file_path| details. 108// 109// Called on the UI thread to dispatch the request to the 110// MediaTransferProtocolManager. 111// 112// |storage_name| specifies the name of the storage device. 113// |success_callback| is called when the GetFileInfo request succeeds. 114// |error_callback| is called when the GetFileInfo request fails. 115// |success_callback| and |error_callback| runs on the IO thread. 116void GetFileInfoOnUIThread( 117 const std::string& storage_name, 118 uint32 file_id, 119 const MTPDeviceTaskHelper::GetFileInfoSuccessCallback& success_callback, 120 const MTPDeviceTaskHelper::ErrorCallback& error_callback) { 121 DCHECK_CURRENTLY_ON(content::BrowserThread::UI); 122 MTPDeviceTaskHelper* task_helper = 123 GetDeviceTaskHelperForStorage(storage_name); 124 if (!task_helper) 125 return; 126 task_helper->GetFileInfo(file_id, success_callback, error_callback); 127} 128 129// Copies the contents of |device_file_path| to |snapshot_file_path|. 130// 131// Called on the UI thread to dispatch the request to the 132// MediaTransferProtocolManager. 133// 134// |storage_name| specifies the name of the storage device. 135// |device_file_path| specifies the media device file path. 136// |snapshot_file_path| specifies the platform path of the snapshot file. 137// |file_size| specifies the number of bytes that will be written to the 138// snapshot file. 139// |success_callback| is called when the copy operation succeeds. 140// |error_callback| is called when the copy operation fails. 141// |success_callback| and |error_callback| runs on the IO thread. 142void WriteDataIntoSnapshotFileOnUIThread( 143 const std::string& storage_name, 144 const SnapshotRequestInfo& request_info, 145 const base::File::Info& snapshot_file_info) { 146 DCHECK_CURRENTLY_ON(content::BrowserThread::UI); 147 MTPDeviceTaskHelper* task_helper = 148 GetDeviceTaskHelperForStorage(storage_name); 149 if (!task_helper) 150 return; 151 task_helper->WriteDataIntoSnapshotFile(request_info, snapshot_file_info); 152} 153 154// Copies the contents of |device_file_path| to |snapshot_file_path|. 155// 156// Called on the UI thread to dispatch the request to the 157// MediaTransferProtocolManager. 158// 159// |storage_name| specifies the name of the storage device. 160// |request| is a struct containing details about the byte read request. 161void ReadBytesOnUIThread( 162 const std::string& storage_name, 163 const MTPDeviceAsyncDelegate::ReadBytesRequest& request) { 164 DCHECK_CURRENTLY_ON(content::BrowserThread::UI); 165 MTPDeviceTaskHelper* task_helper = 166 GetDeviceTaskHelperForStorage(storage_name); 167 if (!task_helper) 168 return; 169 task_helper->ReadBytes(request); 170} 171 172// Closes the device storage specified by the |storage_name| and destroys the 173// MTPDeviceTaskHelper object associated with the device storage. 174// 175// Called on the UI thread to dispatch the request to the 176// MediaTransferProtocolManager. 177void CloseStorageAndDestroyTaskHelperOnUIThread( 178 const std::string& storage_name) { 179 DCHECK_CURRENTLY_ON(content::BrowserThread::UI); 180 MTPDeviceTaskHelper* task_helper = 181 GetDeviceTaskHelperForStorage(storage_name); 182 if (!task_helper) 183 return; 184 task_helper->CloseStorage(); 185 MTPDeviceTaskHelperMapService::GetInstance()->DestroyDeviceTaskHelper( 186 storage_name); 187} 188 189} // namespace 190 191MTPDeviceDelegateImplLinux::PendingTaskInfo::PendingTaskInfo( 192 const base::FilePath& path, 193 content::BrowserThread::ID thread_id, 194 const tracked_objects::Location& location, 195 const base::Closure& task) 196 : path(path), 197 thread_id(thread_id), 198 location(location), 199 task(task) { 200} 201 202MTPDeviceDelegateImplLinux::PendingTaskInfo::~PendingTaskInfo() { 203} 204 205// Represents a file on the MTP device. 206// Lives on the IO thread. 207class MTPDeviceDelegateImplLinux::MTPFileNode { 208 public: 209 MTPFileNode(uint32 file_id, 210 const std::string& file_name, 211 MTPFileNode* parent, 212 FileIdToMTPFileNodeMap* file_id_to_node_map); 213 ~MTPFileNode(); 214 215 const MTPFileNode* GetChild(const std::string& name) const; 216 217 void EnsureChildExists(const std::string& name, uint32 id); 218 219 // Clears all the children, except those in |children_to_keep|. 220 void ClearNonexistentChildren( 221 const std::set<std::string>& children_to_keep); 222 223 bool DeleteChild(uint32 file_id); 224 225 uint32 file_id() const { return file_id_; } 226 const std::string& file_name() const { return file_name_; } 227 MTPFileNode* parent() { return parent_; } 228 229 private: 230 // Container for holding a node's children. 231 typedef base::ScopedPtrHashMap<std::string, MTPFileNode> ChildNodes; 232 233 const uint32 file_id_; 234 const std::string file_name_; 235 236 ChildNodes children_; 237 MTPFileNode* const parent_; 238 FileIdToMTPFileNodeMap* file_id_to_node_map_; 239 240 DISALLOW_COPY_AND_ASSIGN(MTPFileNode); 241}; 242 243MTPDeviceDelegateImplLinux::MTPFileNode::MTPFileNode( 244 uint32 file_id, 245 const std::string& file_name, 246 MTPFileNode* parent, 247 FileIdToMTPFileNodeMap* file_id_to_node_map) 248 : file_id_(file_id), 249 file_name_(file_name), 250 parent_(parent), 251 file_id_to_node_map_(file_id_to_node_map) { 252 DCHECK_CURRENTLY_ON(content::BrowserThread::IO); 253 DCHECK(file_id_to_node_map_); 254 DCHECK(!ContainsKey(*file_id_to_node_map_, file_id_)); 255 (*file_id_to_node_map_)[file_id_] = this; 256} 257 258MTPDeviceDelegateImplLinux::MTPFileNode::~MTPFileNode() { 259 DCHECK_CURRENTLY_ON(content::BrowserThread::IO); 260 size_t erased = file_id_to_node_map_->erase(file_id_); 261 DCHECK_EQ(1U, erased); 262} 263 264const MTPDeviceDelegateImplLinux::MTPFileNode* 265MTPDeviceDelegateImplLinux::MTPFileNode::GetChild( 266 const std::string& name) const { 267 DCHECK_CURRENTLY_ON(content::BrowserThread::IO); 268 return children_.get(name); 269} 270 271void MTPDeviceDelegateImplLinux::MTPFileNode::EnsureChildExists( 272 const std::string& name, 273 uint32 id) { 274 DCHECK_CURRENTLY_ON(content::BrowserThread::IO); 275 const MTPFileNode* child = GetChild(name); 276 if (child && child->file_id() == id) 277 return; 278 279 children_.set( 280 name, 281 make_scoped_ptr(new MTPFileNode(id, name, this, file_id_to_node_map_))); 282} 283 284void MTPDeviceDelegateImplLinux::MTPFileNode::ClearNonexistentChildren( 285 const std::set<std::string>& children_to_keep) { 286 DCHECK_CURRENTLY_ON(content::BrowserThread::IO); 287 std::set<std::string> children_to_erase; 288 for (ChildNodes::const_iterator it = children_.begin(); 289 it != children_.end(); ++it) { 290 if (ContainsKey(children_to_keep, it->first)) 291 continue; 292 children_to_erase.insert(it->first); 293 } 294 for (std::set<std::string>::iterator it = children_to_erase.begin(); 295 it != children_to_erase.end(); ++it) { 296 children_.take_and_erase(*it); 297 } 298} 299 300bool MTPDeviceDelegateImplLinux::MTPFileNode::DeleteChild(uint32 file_id) { 301 DCHECK_CURRENTLY_ON(content::BrowserThread::IO); 302 for (ChildNodes::iterator it = children_.begin(); 303 it != children_.end(); ++it) { 304 if (it->second->file_id() == file_id) { 305 children_.erase(it); 306 return true; 307 } 308 } 309 return false; 310} 311 312MTPDeviceDelegateImplLinux::MTPDeviceDelegateImplLinux( 313 const std::string& device_location) 314 : init_state_(UNINITIALIZED), 315 task_in_progress_(false), 316 device_path_(device_location), 317 root_node_(new MTPFileNode(mtpd::kRootFileId, 318 "", // Root node has no name. 319 NULL, // And no parent node. 320 &file_id_to_node_map_)), 321 weak_ptr_factory_(this) { 322 DCHECK_CURRENTLY_ON(content::BrowserThread::IO); 323 DCHECK(!device_path_.empty()); 324 base::RemoveChars(device_location, kRootPath, &storage_name_); 325 DCHECK(!storage_name_.empty()); 326} 327 328MTPDeviceDelegateImplLinux::~MTPDeviceDelegateImplLinux() { 329 DCHECK_CURRENTLY_ON(content::BrowserThread::IO); 330} 331 332void MTPDeviceDelegateImplLinux::GetFileInfo( 333 const base::FilePath& file_path, 334 const GetFileInfoSuccessCallback& success_callback, 335 const ErrorCallback& error_callback) { 336 DCHECK_CURRENTLY_ON(content::BrowserThread::IO); 337 DCHECK(!file_path.empty()); 338 339 // If a ReadDirectory operation is in progress, the file info may already be 340 // cached. 341 FileInfoCache::const_iterator it = file_info_cache_.find(file_path); 342 if (it != file_info_cache_.end()) { 343 // TODO(thestig): This code is repeated in several places. Combine them. 344 // e.g. c/b/media_galleries/win/mtp_device_operations_util.cc 345 const storage::DirectoryEntry& cached_file_entry = it->second; 346 base::File::Info info; 347 info.size = cached_file_entry.size; 348 info.is_directory = cached_file_entry.is_directory; 349 info.is_symbolic_link = false; 350 info.last_modified = cached_file_entry.last_modified_time; 351 info.creation_time = base::Time(); 352 353 success_callback.Run(info); 354 return; 355 } 356 base::Closure closure = 357 base::Bind(&MTPDeviceDelegateImplLinux::GetFileInfoInternal, 358 weak_ptr_factory_.GetWeakPtr(), 359 file_path, 360 success_callback, 361 error_callback); 362 EnsureInitAndRunTask(PendingTaskInfo(file_path, 363 content::BrowserThread::IO, 364 FROM_HERE, 365 closure)); 366} 367 368void MTPDeviceDelegateImplLinux::ReadDirectory( 369 const base::FilePath& root, 370 const ReadDirectorySuccessCallback& success_callback, 371 const ErrorCallback& error_callback) { 372 DCHECK_CURRENTLY_ON(content::BrowserThread::IO); 373 DCHECK(!root.empty()); 374 base::Closure closure = 375 base::Bind(&MTPDeviceDelegateImplLinux::ReadDirectoryInternal, 376 weak_ptr_factory_.GetWeakPtr(), 377 root, 378 success_callback, 379 error_callback); 380 EnsureInitAndRunTask(PendingTaskInfo(root, 381 content::BrowserThread::IO, 382 FROM_HERE, 383 closure)); 384} 385 386void MTPDeviceDelegateImplLinux::CreateSnapshotFile( 387 const base::FilePath& device_file_path, 388 const base::FilePath& local_path, 389 const CreateSnapshotFileSuccessCallback& success_callback, 390 const ErrorCallback& error_callback) { 391 DCHECK_CURRENTLY_ON(content::BrowserThread::IO); 392 DCHECK(!device_file_path.empty()); 393 DCHECK(!local_path.empty()); 394 base::Closure closure = 395 base::Bind(&MTPDeviceDelegateImplLinux::CreateSnapshotFileInternal, 396 weak_ptr_factory_.GetWeakPtr(), 397 device_file_path, 398 local_path, 399 success_callback, 400 error_callback); 401 EnsureInitAndRunTask(PendingTaskInfo(device_file_path, 402 content::BrowserThread::IO, 403 FROM_HERE, 404 closure)); 405} 406 407bool MTPDeviceDelegateImplLinux::IsStreaming() { 408 return true; 409} 410 411void MTPDeviceDelegateImplLinux::ReadBytes( 412 const base::FilePath& device_file_path, 413 const scoped_refptr<net::IOBuffer>& buf, 414 int64 offset, 415 int buf_len, 416 const ReadBytesSuccessCallback& success_callback, 417 const ErrorCallback& error_callback) { 418 DCHECK_CURRENTLY_ON(content::BrowserThread::IO); 419 DCHECK(!device_file_path.empty()); 420 base::Closure closure = 421 base::Bind(&MTPDeviceDelegateImplLinux::ReadBytesInternal, 422 weak_ptr_factory_.GetWeakPtr(), 423 device_file_path, 424 buf, 425 offset, 426 buf_len, 427 success_callback, 428 error_callback); 429 EnsureInitAndRunTask(PendingTaskInfo(device_file_path, 430 content::BrowserThread::IO, 431 FROM_HERE, 432 closure)); 433} 434 435void MTPDeviceDelegateImplLinux::CancelPendingTasksAndDeleteDelegate() { 436 DCHECK_CURRENTLY_ON(content::BrowserThread::IO); 437 // To cancel all the pending tasks, destroy the MTPDeviceTaskHelper object. 438 content::BrowserThread::PostTask( 439 content::BrowserThread::UI, 440 FROM_HERE, 441 base::Bind(&CloseStorageAndDestroyTaskHelperOnUIThread, storage_name_)); 442 delete this; 443} 444 445void MTPDeviceDelegateImplLinux::GetFileInfoInternal( 446 const base::FilePath& file_path, 447 const GetFileInfoSuccessCallback& success_callback, 448 const ErrorCallback& error_callback) { 449 DCHECK_CURRENTLY_ON(content::BrowserThread::IO); 450 451 uint32 file_id; 452 if (CachedPathToId(file_path, &file_id)) { 453 GetFileInfoSuccessCallback success_callback_wrapper = 454 base::Bind(&MTPDeviceDelegateImplLinux::OnDidGetFileInfo, 455 weak_ptr_factory_.GetWeakPtr(), 456 success_callback); 457 ErrorCallback error_callback_wrapper = 458 base::Bind(&MTPDeviceDelegateImplLinux::HandleDeviceFileError, 459 weak_ptr_factory_.GetWeakPtr(), 460 error_callback, 461 file_id); 462 463 464 base::Closure closure = base::Bind(&GetFileInfoOnUIThread, 465 storage_name_, 466 file_id, 467 success_callback_wrapper, 468 error_callback_wrapper); 469 EnsureInitAndRunTask(PendingTaskInfo(base::FilePath(), 470 content::BrowserThread::UI, 471 FROM_HERE, 472 closure)); 473 } else { 474 error_callback.Run(base::File::FILE_ERROR_NOT_FOUND); 475 } 476 PendingRequestDone(); 477} 478 479void MTPDeviceDelegateImplLinux::ReadDirectoryInternal( 480 const base::FilePath& root, 481 const ReadDirectorySuccessCallback& success_callback, 482 const ErrorCallback& error_callback) { 483 DCHECK_CURRENTLY_ON(content::BrowserThread::IO); 484 485 uint32 dir_id; 486 if (CachedPathToId(root, &dir_id)) { 487 GetFileInfoSuccessCallback success_callback_wrapper = 488 base::Bind(&MTPDeviceDelegateImplLinux::OnDidGetFileInfoToReadDirectory, 489 weak_ptr_factory_.GetWeakPtr(), 490 dir_id, 491 success_callback, 492 error_callback); 493 ErrorCallback error_callback_wrapper = 494 base::Bind(&MTPDeviceDelegateImplLinux::HandleDeviceFileError, 495 weak_ptr_factory_.GetWeakPtr(), 496 error_callback, 497 dir_id); 498 base::Closure closure = base::Bind(&GetFileInfoOnUIThread, 499 storage_name_, 500 dir_id, 501 success_callback_wrapper, 502 error_callback_wrapper); 503 EnsureInitAndRunTask(PendingTaskInfo(base::FilePath(), 504 content::BrowserThread::UI, 505 FROM_HERE, 506 closure)); 507 } else { 508 error_callback.Run(base::File::FILE_ERROR_NOT_FOUND); 509 } 510 PendingRequestDone(); 511} 512 513void MTPDeviceDelegateImplLinux::CreateSnapshotFileInternal( 514 const base::FilePath& device_file_path, 515 const base::FilePath& local_path, 516 const CreateSnapshotFileSuccessCallback& success_callback, 517 const ErrorCallback& error_callback) { 518 DCHECK_CURRENTLY_ON(content::BrowserThread::IO); 519 520 uint32 file_id; 521 if (CachedPathToId(device_file_path, &file_id)) { 522 scoped_ptr<SnapshotRequestInfo> request_info( 523 new SnapshotRequestInfo(file_id, 524 local_path, 525 success_callback, 526 error_callback)); 527 GetFileInfoSuccessCallback success_callback_wrapper = 528 base::Bind( 529 &MTPDeviceDelegateImplLinux::OnDidGetFileInfoToCreateSnapshotFile, 530 weak_ptr_factory_.GetWeakPtr(), 531 base::Passed(&request_info)); 532 ErrorCallback error_callback_wrapper = 533 base::Bind(&MTPDeviceDelegateImplLinux::HandleDeviceFileError, 534 weak_ptr_factory_.GetWeakPtr(), 535 error_callback, 536 file_id); 537 base::Closure closure = base::Bind(&GetFileInfoOnUIThread, 538 storage_name_, 539 file_id, 540 success_callback_wrapper, 541 error_callback_wrapper); 542 EnsureInitAndRunTask(PendingTaskInfo(base::FilePath(), 543 content::BrowserThread::UI, 544 FROM_HERE, 545 closure)); 546 } else { 547 error_callback.Run(base::File::FILE_ERROR_NOT_FOUND); 548 } 549 PendingRequestDone(); 550} 551 552void MTPDeviceDelegateImplLinux::ReadBytesInternal( 553 const base::FilePath& device_file_path, 554 net::IOBuffer* buf, int64 offset, int buf_len, 555 const ReadBytesSuccessCallback& success_callback, 556 const ErrorCallback& error_callback) { 557 DCHECK_CURRENTLY_ON(content::BrowserThread::IO); 558 559 uint32 file_id; 560 if (CachedPathToId(device_file_path, &file_id)) { 561 ReadBytesRequest request( 562 file_id, buf, offset, buf_len, 563 base::Bind(&MTPDeviceDelegateImplLinux::OnDidReadBytes, 564 weak_ptr_factory_.GetWeakPtr(), 565 success_callback), 566 base::Bind(&MTPDeviceDelegateImplLinux::HandleDeviceFileError, 567 weak_ptr_factory_.GetWeakPtr(), 568 error_callback, 569 file_id)); 570 571 base::Closure closure = 572 base::Bind(base::Bind(&ReadBytesOnUIThread, storage_name_, request)); 573 EnsureInitAndRunTask(PendingTaskInfo(base::FilePath(), 574 content::BrowserThread::UI, 575 FROM_HERE, 576 closure)); 577 } else { 578 error_callback.Run(base::File::FILE_ERROR_NOT_FOUND); 579 } 580 PendingRequestDone(); 581} 582 583void MTPDeviceDelegateImplLinux::EnsureInitAndRunTask( 584 const PendingTaskInfo& task_info) { 585 DCHECK_CURRENTLY_ON(content::BrowserThread::IO); 586 if ((init_state_ == INITIALIZED) && !task_in_progress_) { 587 RunTask(task_info); 588 return; 589 } 590 591 // Only *Internal functions have empty paths. Since they are the continuation 592 // of the current running task, they get to cut in line. 593 if (task_info.path.empty()) 594 pending_tasks_.push_front(task_info); 595 else 596 pending_tasks_.push_back(task_info); 597 598 if (init_state_ == UNINITIALIZED) { 599 init_state_ = PENDING_INIT; 600 task_in_progress_ = true; 601 content::BrowserThread::PostTask( 602 content::BrowserThread::UI, 603 FROM_HERE, 604 base::Bind(&OpenStorageOnUIThread, 605 storage_name_, 606 base::Bind(&MTPDeviceDelegateImplLinux::OnInitCompleted, 607 weak_ptr_factory_.GetWeakPtr()))); 608 } 609} 610 611void MTPDeviceDelegateImplLinux::RunTask(const PendingTaskInfo& task_info) { 612 DCHECK_CURRENTLY_ON(content::BrowserThread::IO); 613 DCHECK_EQ(INITIALIZED, init_state_); 614 DCHECK(!task_in_progress_); 615 task_in_progress_ = true; 616 617 bool need_to_check_cache = !task_info.path.empty(); 618 if (need_to_check_cache) { 619 base::FilePath uncached_path = 620 NextUncachedPathComponent(task_info.path, task_info.cached_path); 621 if (!uncached_path.empty()) { 622 // Save the current task and do a cache lookup first. 623 pending_tasks_.push_front(task_info); 624 FillFileCache(uncached_path); 625 return; 626 } 627 } 628 629 content::BrowserThread::PostTask(task_info.thread_id, 630 task_info.location, 631 task_info.task); 632} 633 634void MTPDeviceDelegateImplLinux::WriteDataIntoSnapshotFile( 635 const base::File::Info& file_info) { 636 DCHECK_CURRENTLY_ON(content::BrowserThread::IO); 637 DCHECK(current_snapshot_request_info_.get()); 638 DCHECK_GT(file_info.size, 0); 639 DCHECK(task_in_progress_); 640 SnapshotRequestInfo request_info( 641 current_snapshot_request_info_->file_id, 642 current_snapshot_request_info_->snapshot_file_path, 643 base::Bind( 644 &MTPDeviceDelegateImplLinux::OnDidWriteDataIntoSnapshotFile, 645 weak_ptr_factory_.GetWeakPtr()), 646 base::Bind( 647 &MTPDeviceDelegateImplLinux::OnWriteDataIntoSnapshotFileError, 648 weak_ptr_factory_.GetWeakPtr())); 649 650 base::Closure task_closure = base::Bind(&WriteDataIntoSnapshotFileOnUIThread, 651 storage_name_, 652 request_info, 653 file_info); 654 content::BrowserThread::PostTask(content::BrowserThread::UI, 655 FROM_HERE, 656 task_closure); 657} 658 659void MTPDeviceDelegateImplLinux::PendingRequestDone() { 660 DCHECK(task_in_progress_); 661 task_in_progress_ = false; 662 ProcessNextPendingRequest(); 663} 664 665void MTPDeviceDelegateImplLinux::ProcessNextPendingRequest() { 666 DCHECK_CURRENTLY_ON(content::BrowserThread::IO); 667 DCHECK(!task_in_progress_); 668 if (pending_tasks_.empty()) 669 return; 670 671 PendingTaskInfo task_info = pending_tasks_.front(); 672 pending_tasks_.pop_front(); 673 RunTask(task_info); 674} 675 676void MTPDeviceDelegateImplLinux::OnInitCompleted(bool succeeded) { 677 DCHECK_CURRENTLY_ON(content::BrowserThread::IO); 678 init_state_ = succeeded ? INITIALIZED : UNINITIALIZED; 679 PendingRequestDone(); 680} 681 682void MTPDeviceDelegateImplLinux::OnDidGetFileInfo( 683 const GetFileInfoSuccessCallback& success_callback, 684 const base::File::Info& file_info) { 685 DCHECK_CURRENTLY_ON(content::BrowserThread::IO); 686 success_callback.Run(file_info); 687 PendingRequestDone(); 688} 689 690void MTPDeviceDelegateImplLinux::OnDidGetFileInfoToReadDirectory( 691 uint32 dir_id, 692 const ReadDirectorySuccessCallback& success_callback, 693 const ErrorCallback& error_callback, 694 const base::File::Info& file_info) { 695 DCHECK_CURRENTLY_ON(content::BrowserThread::IO); 696 DCHECK(task_in_progress_); 697 if (!file_info.is_directory) { 698 return HandleDeviceFileError(error_callback, 699 dir_id, 700 base::File::FILE_ERROR_NOT_A_DIRECTORY); 701 } 702 703 base::Closure task_closure = 704 base::Bind(&ReadDirectoryOnUIThread, 705 storage_name_, 706 dir_id, 707 base::Bind(&MTPDeviceDelegateImplLinux::OnDidReadDirectory, 708 weak_ptr_factory_.GetWeakPtr(), 709 dir_id, 710 success_callback), 711 base::Bind(&MTPDeviceDelegateImplLinux::HandleDeviceFileError, 712 weak_ptr_factory_.GetWeakPtr(), 713 error_callback, 714 dir_id)); 715 content::BrowserThread::PostTask(content::BrowserThread::UI, 716 FROM_HERE, 717 task_closure); 718} 719 720void MTPDeviceDelegateImplLinux::OnDidGetFileInfoToCreateSnapshotFile( 721 scoped_ptr<SnapshotRequestInfo> snapshot_request_info, 722 const base::File::Info& file_info) { 723 DCHECK_CURRENTLY_ON(content::BrowserThread::IO); 724 DCHECK(!current_snapshot_request_info_.get()); 725 DCHECK(snapshot_request_info.get()); 726 DCHECK(task_in_progress_); 727 base::File::Error error = base::File::FILE_OK; 728 if (file_info.is_directory) 729 error = base::File::FILE_ERROR_NOT_A_FILE; 730 else if (file_info.size < 0 || file_info.size > kuint32max) 731 error = base::File::FILE_ERROR_FAILED; 732 733 if (error != base::File::FILE_OK) 734 return HandleDeviceFileError(snapshot_request_info->error_callback, 735 snapshot_request_info->file_id, 736 error); 737 738 base::File::Info snapshot_file_info(file_info); 739 // Modify the last modified time to null. This prevents the time stamp 740 // verfication in LocalFileStreamReader. 741 snapshot_file_info.last_modified = base::Time(); 742 743 current_snapshot_request_info_.reset(snapshot_request_info.release()); 744 if (file_info.size == 0) { 745 // Empty snapshot file. 746 return OnDidWriteDataIntoSnapshotFile( 747 snapshot_file_info, current_snapshot_request_info_->snapshot_file_path); 748 } 749 WriteDataIntoSnapshotFile(snapshot_file_info); 750} 751 752void MTPDeviceDelegateImplLinux::OnDidReadDirectory( 753 uint32 dir_id, 754 const ReadDirectorySuccessCallback& success_callback, 755 const storage::AsyncFileUtil::EntryList& file_list, 756 bool has_more) { 757 DCHECK_CURRENTLY_ON(content::BrowserThread::IO); 758 759 FileIdToMTPFileNodeMap::iterator it = file_id_to_node_map_.find(dir_id); 760 DCHECK(it != file_id_to_node_map_.end()); 761 MTPFileNode* dir_node = it->second; 762 763 // Traverse the MTPFileNode tree to reconstuct the full path for |dir_id|. 764 std::deque<std::string> dir_path_parts; 765 MTPFileNode* parent_node = dir_node; 766 while (parent_node->parent()) { 767 dir_path_parts.push_front(parent_node->file_name()); 768 parent_node = parent_node->parent(); 769 } 770 base::FilePath dir_path = device_path_; 771 for (size_t i = 0; i < dir_path_parts.size(); ++i) 772 dir_path = dir_path.Append(dir_path_parts[i]); 773 774 storage::AsyncFileUtil::EntryList normalized_file_list; 775 for (size_t i = 0; i < file_list.size(); ++i) { 776 normalized_file_list.push_back(file_list[i]); 777 storage::DirectoryEntry& entry = normalized_file_list.back(); 778 779 // |entry.name| has the file id encoded in it. Decode here. 780 size_t separator_idx = entry.name.find_last_of(','); 781 DCHECK_NE(std::string::npos, separator_idx); 782 std::string file_id_str = entry.name.substr(separator_idx); 783 file_id_str = file_id_str.substr(1); // Get rid of the comma. 784 uint32 file_id = 0; 785 bool ret = base::StringToUint(file_id_str, &file_id); 786 DCHECK(ret); 787 entry.name = entry.name.substr(0, separator_idx); 788 789 // Refresh the in memory tree. 790 dir_node->EnsureChildExists(entry.name, file_id); 791 child_nodes_seen_.insert(entry.name); 792 793 // Add to |file_info_cache_|. 794 file_info_cache_[dir_path.Append(entry.name)] = entry; 795 } 796 797 success_callback.Run(normalized_file_list, has_more); 798 if (has_more) 799 return; // Wait to be called again. 800 801 // Last call, finish book keeping and continue with the next request. 802 dir_node->ClearNonexistentChildren(child_nodes_seen_); 803 child_nodes_seen_.clear(); 804 file_info_cache_.clear(); 805 806 PendingRequestDone(); 807} 808 809void MTPDeviceDelegateImplLinux::OnDidWriteDataIntoSnapshotFile( 810 const base::File::Info& file_info, 811 const base::FilePath& snapshot_file_path) { 812 DCHECK_CURRENTLY_ON(content::BrowserThread::IO); 813 DCHECK(current_snapshot_request_info_.get()); 814 current_snapshot_request_info_->success_callback.Run( 815 file_info, snapshot_file_path); 816 current_snapshot_request_info_.reset(); 817 PendingRequestDone(); 818} 819 820void MTPDeviceDelegateImplLinux::OnWriteDataIntoSnapshotFileError( 821 base::File::Error error) { 822 DCHECK_CURRENTLY_ON(content::BrowserThread::IO); 823 DCHECK(current_snapshot_request_info_.get()); 824 current_snapshot_request_info_->error_callback.Run(error); 825 current_snapshot_request_info_.reset(); 826 PendingRequestDone(); 827} 828 829void MTPDeviceDelegateImplLinux::OnDidReadBytes( 830 const ReadBytesSuccessCallback& success_callback, 831 const base::File::Info& file_info, int bytes_read) { 832 DCHECK_CURRENTLY_ON(content::BrowserThread::IO); 833 success_callback.Run(file_info, bytes_read); 834 PendingRequestDone(); 835} 836 837void MTPDeviceDelegateImplLinux::OnDidFillFileCache( 838 const base::FilePath& path, 839 const storage::AsyncFileUtil::EntryList& /* file_list */, 840 bool has_more) { 841 DCHECK_CURRENTLY_ON(content::BrowserThread::IO); 842 DCHECK(path.IsParent(pending_tasks_.front().path)); 843 if (has_more) 844 return; // Wait until all entries have been read. 845 pending_tasks_.front().cached_path = path; 846} 847 848void MTPDeviceDelegateImplLinux::OnFillFileCacheFailed( 849 base::File::Error /* error */) { 850 DCHECK_CURRENTLY_ON(content::BrowserThread::IO); 851 // When filling the cache fails for the task at the front of the queue, clear 852 // the path of the task so it will not try to do any more caching. Instead, 853 // the task will just run and fail the CachedPathToId() lookup. 854 pending_tasks_.front().path.clear(); 855} 856 857void MTPDeviceDelegateImplLinux::HandleDeviceFileError( 858 const ErrorCallback& error_callback, 859 uint32 file_id, 860 base::File::Error error) { 861 DCHECK_CURRENTLY_ON(content::BrowserThread::IO); 862 863 FileIdToMTPFileNodeMap::iterator it = file_id_to_node_map_.find(file_id); 864 if (it != file_id_to_node_map_.end()) { 865 MTPFileNode* parent = it->second->parent(); 866 if (parent) { 867 bool ret = parent->DeleteChild(file_id); 868 DCHECK(ret); 869 } 870 } 871 error_callback.Run(error); 872 PendingRequestDone(); 873} 874 875base::FilePath MTPDeviceDelegateImplLinux::NextUncachedPathComponent( 876 const base::FilePath& path, 877 const base::FilePath& cached_path) const { 878 DCHECK_CURRENTLY_ON(content::BrowserThread::IO); 879 DCHECK(cached_path.empty() || cached_path.IsParent(path)); 880 881 base::FilePath uncached_path; 882 std::string device_relpath = GetDeviceRelativePath(device_path_, path); 883 if (!device_relpath.empty() && device_relpath != kRootPath) { 884 uncached_path = device_path_; 885 std::vector<std::string> device_relpath_components; 886 base::SplitString(device_relpath, '/', &device_relpath_components); 887 DCHECK(!device_relpath_components.empty()); 888 bool all_components_cached = true; 889 const MTPFileNode* current_node = root_node_.get(); 890 for (size_t i = 0; i < device_relpath_components.size(); ++i) { 891 current_node = current_node->GetChild(device_relpath_components[i]); 892 if (!current_node) { 893 // With a cache miss, check if it is a genuine failure. If so, pretend 894 // the entire |path| is cached, so there is no further attempt to do 895 // more caching. The actual operation will then fail. 896 all_components_cached = 897 !cached_path.empty() && (uncached_path == cached_path); 898 break; 899 } 900 uncached_path = uncached_path.Append(device_relpath_components[i]); 901 } 902 if (all_components_cached) 903 uncached_path.clear(); 904 } 905 return uncached_path; 906} 907 908void MTPDeviceDelegateImplLinux::FillFileCache( 909 const base::FilePath& uncached_path) { 910 DCHECK_CURRENTLY_ON(content::BrowserThread::IO); 911 DCHECK(task_in_progress_); 912 913 ReadDirectorySuccessCallback success_callback = 914 base::Bind(&MTPDeviceDelegateImplLinux::OnDidFillFileCache, 915 weak_ptr_factory_.GetWeakPtr(), 916 uncached_path); 917 ErrorCallback error_callback = 918 base::Bind(&MTPDeviceDelegateImplLinux::OnFillFileCacheFailed, 919 weak_ptr_factory_.GetWeakPtr()); 920 ReadDirectoryInternal(uncached_path, success_callback, error_callback); 921} 922 923 924bool MTPDeviceDelegateImplLinux::CachedPathToId(const base::FilePath& path, 925 uint32* id) const { 926 DCHECK(id); 927 928 std::string device_relpath = GetDeviceRelativePath(device_path_, path); 929 if (device_relpath.empty()) 930 return false; 931 std::vector<std::string> device_relpath_components; 932 if (device_relpath != kRootPath) 933 base::SplitString(device_relpath, '/', &device_relpath_components); 934 const MTPFileNode* current_node = root_node_.get(); 935 for (size_t i = 0; i < device_relpath_components.size(); ++i) { 936 current_node = current_node->GetChild(device_relpath_components[i]); 937 if (!current_node) 938 return false; 939 } 940 *id = current_node->file_id(); 941 return true; 942} 943 944void CreateMTPDeviceAsyncDelegate( 945 const std::string& device_location, 946 const CreateMTPDeviceAsyncDelegateCallback& callback) { 947 DCHECK_CURRENTLY_ON(content::BrowserThread::IO); 948 callback.Run(new MTPDeviceDelegateImplLinux(device_location)); 949} 950