Home | History | Annotate | Download | only in linux
      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 
     21 namespace {
     22 
     23 // File path separator constant.
     24 const 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|.
     32 std::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).
     56 MTPDeviceTaskHelper* 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.
     71 void 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.
     94 void 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.
    116 void 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.
    142 void 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.
    161 void 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.
    177 void 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 
    191 MTPDeviceDelegateImplLinux::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 
    202 MTPDeviceDelegateImplLinux::PendingTaskInfo::~PendingTaskInfo() {
    203 }
    204 
    205 // Represents a file on the MTP device.
    206 // Lives on the IO thread.
    207 class 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 
    243 MTPDeviceDelegateImplLinux::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 
    258 MTPDeviceDelegateImplLinux::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 
    264 const MTPDeviceDelegateImplLinux::MTPFileNode*
    265 MTPDeviceDelegateImplLinux::MTPFileNode::GetChild(
    266     const std::string& name) const {
    267   DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
    268   return children_.get(name);
    269 }
    270 
    271 void 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 
    284 void 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 
    300 bool 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 
    312 MTPDeviceDelegateImplLinux::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 
    328 MTPDeviceDelegateImplLinux::~MTPDeviceDelegateImplLinux() {
    329   DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
    330 }
    331 
    332 void 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 
    368 void 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 
    386 void 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 
    407 bool MTPDeviceDelegateImplLinux::IsStreaming() {
    408   return true;
    409 }
    410 
    411 void 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 
    435 void 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 
    445 void 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 
    479 void 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 
    513 void 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 
    552 void 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 
    583 void 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 
    611 void 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 
    634 void 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 
    659 void MTPDeviceDelegateImplLinux::PendingRequestDone() {
    660   DCHECK(task_in_progress_);
    661   task_in_progress_ = false;
    662   ProcessNextPendingRequest();
    663 }
    664 
    665 void 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 
    676 void MTPDeviceDelegateImplLinux::OnInitCompleted(bool succeeded) {
    677   DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
    678   init_state_ = succeeded ? INITIALIZED : UNINITIALIZED;
    679   PendingRequestDone();
    680 }
    681 
    682 void 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 
    690 void 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 
    720 void 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 
    752 void 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 
    809 void 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 
    820 void 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 
    829 void 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 
    837 void 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 
    848 void 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 
    857 void 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 
    875 base::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 
    908 void 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 
    924 bool 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 
    944 void 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