Upstream version 10.38.208.0
[platform/framework/web/crosswalk.git] / src / chrome / browser / media_galleries / linux / mtp_device_delegate_impl_linux.cc
index 2d74f1d..947068f 100644 (file)
@@ -4,13 +4,19 @@
 
 #include "chrome/browser/media_galleries/linux/mtp_device_delegate_impl_linux.h"
 
+#include <algorithm>
+#include <vector>
+
 #include "base/bind.h"
-#include "base/files/file_path.h"
+#include "base/numerics/safe_conversions.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/strings/string_split.h"
 #include "base/strings/string_util.h"
 #include "chrome/browser/media_galleries/linux/mtp_device_task_helper.h"
 #include "chrome/browser/media_galleries/linux/mtp_device_task_helper_map_service.h"
 #include "chrome/browser/media_galleries/linux/snapshot_file_details.h"
-#include "content/public/browser/browser_thread.h"
+#include "net/base/io_buffer.h"
+#include "third_party/cros_system_api/dbus/service_constants.h"
 
 namespace {
 
@@ -20,20 +26,25 @@ const char kRootPath[] = "/";
 // Returns the device relative file path given |file_path|.
 // E.g.: If the |file_path| is "/usb:2,2:12345/DCIM" and |registered_dev_path|
 // is "/usb:2,2:12345", this function returns the device relative path which is
-// "/DCIM".
+// "DCIM".
+// In the special case when |registered_dev_path| and |file_path| are the same,
+// return |kRootPath|.
 std::string GetDeviceRelativePath(const base::FilePath& registered_dev_path,
                                   const base::FilePath& file_path) {
-  DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
+  DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
   DCHECK(!registered_dev_path.empty());
   DCHECK(!file_path.empty());
-  if (registered_dev_path == file_path)
-    return kRootPath;
-
-  base::FilePath relative_path;
-  if (!registered_dev_path.AppendRelativePath(file_path, &relative_path))
-    return std::string();
-  DCHECK(!relative_path.empty());
-  return relative_path.value();
+  std::string result;
+  if (registered_dev_path == file_path) {
+    result = kRootPath;
+  } else {
+    base::FilePath relative_path;
+    if (registered_dev_path.AppendRelativePath(file_path, &relative_path)) {
+      DCHECK(!relative_path.empty());
+      result = relative_path.value();
+    }
+  }
+  return result;
 }
 
 // Returns the MTPDeviceTaskHelper object associated with the MTP device
@@ -44,7 +55,7 @@ std::string GetDeviceRelativePath(const base::FilePath& registered_dev_path,
 // corresponding storage device is detached, etc).
 MTPDeviceTaskHelper* GetDeviceTaskHelperForStorage(
     const std::string& storage_name) {
-  DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
   return MTPDeviceTaskHelperMapService::GetInstance()->GetDeviceTaskHelper(
       storage_name);
 }
@@ -59,8 +70,8 @@ MTPDeviceTaskHelper* GetDeviceTaskHelperForStorage(
 // |reply_callback| runs on the IO thread.
 void OpenStorageOnUIThread(
     const std::string& storage_name,
-    const base::Callback<void(bool)>& reply_callback) {
-  DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
+    const MTPDeviceTaskHelper::OpenStorageCallback& reply_callback) {
+  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
   MTPDeviceTaskHelper* task_helper =
       GetDeviceTaskHelperForStorage(storage_name);
   if (!task_helper) {
@@ -71,7 +82,7 @@ void OpenStorageOnUIThread(
   task_helper->OpenStorage(storage_name, reply_callback);
 }
 
-// Enumerates the |root| directory file entries.
+// Enumerates the |dir_id| directory file entries.
 //
 // Called on the UI thread to dispatch the request to the
 // MediaTransferProtocolManager.
@@ -82,16 +93,15 @@ void OpenStorageOnUIThread(
 // |success_callback| and |error_callback| runs on the IO thread.
 void ReadDirectoryOnUIThread(
     const std::string& storage_name,
-    const std::string& root,
-    const base::Callback<
-        void(const fileapi::AsyncFileUtil::EntryList&)>& success_callback,
-    const base::Callback<void(base::PlatformFileError)>& error_callback) {
-  DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
+    uint32 dir_id,
+    const MTPDeviceTaskHelper::ReadDirectorySuccessCallback& success_callback,
+    const MTPDeviceTaskHelper::ErrorCallback& error_callback) {
+  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
   MTPDeviceTaskHelper* task_helper =
       GetDeviceTaskHelperForStorage(storage_name);
   if (!task_helper)
     return;
-  task_helper->ReadDirectoryByPath(root, success_callback, error_callback);
+  task_helper->ReadDirectory(dir_id, success_callback, error_callback);
 }
 
 // Gets the |file_path| details.
@@ -105,15 +115,15 @@ void ReadDirectoryOnUIThread(
 // |success_callback| and |error_callback| runs on the IO thread.
 void GetFileInfoOnUIThread(
     const std::string& storage_name,
-    const std::string& file_path,
-    const base::Callback<void(const base::PlatformFileInfo&)>& success_callback,
-    const base::Callback<void(base::PlatformFileError)>& error_callback) {
-  DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
+    uint32 file_id,
+    const MTPDeviceTaskHelper::GetFileInfoSuccessCallback& success_callback,
+    const MTPDeviceTaskHelper::ErrorCallback& error_callback) {
+  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
   MTPDeviceTaskHelper* task_helper =
       GetDeviceTaskHelperForStorage(storage_name);
   if (!task_helper)
     return;
-  task_helper->GetFileInfoByPath(file_path, success_callback, error_callback);
+  task_helper->GetFileInfo(file_id, success_callback, error_callback);
 }
 
 // Copies the contents of |device_file_path| to |snapshot_file_path|.
@@ -132,8 +142,8 @@ void GetFileInfoOnUIThread(
 void WriteDataIntoSnapshotFileOnUIThread(
     const std::string& storage_name,
     const SnapshotRequestInfo& request_info,
-    const base::PlatformFileInfo& snapshot_file_info) {
-  DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
+    const base::File::Info& snapshot_file_info) {
+  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
   MTPDeviceTaskHelper* task_helper =
       GetDeviceTaskHelperForStorage(storage_name);
   if (!task_helper)
@@ -141,6 +151,24 @@ void WriteDataIntoSnapshotFileOnUIThread(
   task_helper->WriteDataIntoSnapshotFile(request_info, snapshot_file_info);
 }
 
+// Copies the contents of |device_file_path| to |snapshot_file_path|.
+//
+// Called on the UI thread to dispatch the request to the
+// MediaTransferProtocolManager.
+//
+// |storage_name| specifies the name of the storage device.
+// |request| is a struct containing details about the byte read request.
+void ReadBytesOnUIThread(
+    const std::string& storage_name,
+    const MTPDeviceAsyncDelegate::ReadBytesRequest& request) {
+  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
+  MTPDeviceTaskHelper* task_helper =
+      GetDeviceTaskHelperForStorage(storage_name);
+  if (!task_helper)
+    return;
+  task_helper->ReadBytes(request);
+}
+
 // Closes the device storage specified by the |storage_name| and destroys the
 // MTPDeviceTaskHelper object associated with the device storage.
 //
@@ -148,7 +176,7 @@ void WriteDataIntoSnapshotFileOnUIThread(
 // MediaTransferProtocolManager.
 void CloseStorageAndDestroyTaskHelperOnUIThread(
     const std::string& storage_name) {
-  DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
   MTPDeviceTaskHelper* task_helper =
       GetDeviceTaskHelperForStorage(storage_name);
   if (!task_helper)
@@ -161,107 +189,249 @@ void CloseStorageAndDestroyTaskHelperOnUIThread(
 }  // namespace
 
 MTPDeviceDelegateImplLinux::PendingTaskInfo::PendingTaskInfo(
+    const base::FilePath& path,
+    content::BrowserThread::ID thread_id,
     const tracked_objects::Location& location,
     const base::Closure& task)
-    : location(location),
+    : path(path),
+      thread_id(thread_id),
+      location(location),
       task(task) {
 }
 
 MTPDeviceDelegateImplLinux::PendingTaskInfo::~PendingTaskInfo() {
 }
 
+// Represents a file on the MTP device.
+// Lives on the IO thread.
+class MTPDeviceDelegateImplLinux::MTPFileNode {
+ public:
+  MTPFileNode(uint32 file_id,
+              const std::string& file_name,
+              MTPFileNode* parent,
+              FileIdToMTPFileNodeMap* file_id_to_node_map);
+  ~MTPFileNode();
+
+  const MTPFileNode* GetChild(const std::string& name) const;
+
+  void EnsureChildExists(const std::string& name, uint32 id);
+
+  // Clears all the children, except those in |children_to_keep|.
+  void ClearNonexistentChildren(
+      const std::set<std::string>& children_to_keep);
+
+  bool DeleteChild(uint32 file_id);
+
+  uint32 file_id() const { return file_id_; }
+  const std::string& file_name() const { return file_name_; }
+  MTPFileNode* parent() { return parent_; }
+
+ private:
+  // Container for holding a node's children.
+  typedef base::ScopedPtrHashMap<std::string, MTPFileNode> ChildNodes;
+
+  const uint32 file_id_;
+  const std::string file_name_;
+
+  ChildNodes children_;
+  MTPFileNode* const parent_;
+  FileIdToMTPFileNodeMap* file_id_to_node_map_;
+
+  DISALLOW_COPY_AND_ASSIGN(MTPFileNode);
+};
+
+MTPDeviceDelegateImplLinux::MTPFileNode::MTPFileNode(
+    uint32 file_id,
+    const std::string& file_name,
+    MTPFileNode* parent,
+    FileIdToMTPFileNodeMap* file_id_to_node_map)
+    : file_id_(file_id),
+      file_name_(file_name),
+      parent_(parent),
+      file_id_to_node_map_(file_id_to_node_map) {
+  DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
+  DCHECK(file_id_to_node_map_);
+  DCHECK(!ContainsKey(*file_id_to_node_map_, file_id_));
+  (*file_id_to_node_map_)[file_id_] = this;
+}
+
+MTPDeviceDelegateImplLinux::MTPFileNode::~MTPFileNode() {
+  DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
+  size_t erased = file_id_to_node_map_->erase(file_id_);
+  DCHECK_EQ(1U, erased);
+}
+
+const MTPDeviceDelegateImplLinux::MTPFileNode*
+MTPDeviceDelegateImplLinux::MTPFileNode::GetChild(
+    const std::string& name) const {
+  DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
+  return children_.get(name);
+}
+
+void MTPDeviceDelegateImplLinux::MTPFileNode::EnsureChildExists(
+    const std::string& name,
+    uint32 id) {
+  DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
+  const MTPFileNode* child = GetChild(name);
+  if (child && child->file_id() == id)
+    return;
+
+  children_.set(
+      name,
+      make_scoped_ptr(new MTPFileNode(id, name, this, file_id_to_node_map_)));
+}
+
+void MTPDeviceDelegateImplLinux::MTPFileNode::ClearNonexistentChildren(
+    const std::set<std::string>& children_to_keep) {
+  DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
+  std::set<std::string> children_to_erase;
+  for (ChildNodes::const_iterator it = children_.begin();
+       it != children_.end(); ++it) {
+    if (ContainsKey(children_to_keep, it->first))
+      continue;
+    children_to_erase.insert(it->first);
+  }
+  for (std::set<std::string>::iterator it = children_to_erase.begin();
+       it != children_to_erase.end(); ++it) {
+    children_.take_and_erase(*it);
+  }
+}
+
+bool MTPDeviceDelegateImplLinux::MTPFileNode::DeleteChild(uint32 file_id) {
+  DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
+  for (ChildNodes::iterator it = children_.begin();
+       it != children_.end(); ++it) {
+    if (it->second->file_id() == file_id) {
+      children_.erase(it);
+      return true;
+    }
+  }
+  return false;
+}
+
 MTPDeviceDelegateImplLinux::MTPDeviceDelegateImplLinux(
     const std::string& device_location)
     : init_state_(UNINITIALIZED),
       task_in_progress_(false),
       device_path_(device_location),
+      root_node_(new MTPFileNode(mtpd::kRootFileId,
+                                 "",    // Root node has no name.
+                                 NULL,  // And no parent node.
+                                 &file_id_to_node_map_)),
       weak_ptr_factory_(this) {
-  DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
+  DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
   DCHECK(!device_path_.empty());
-  RemoveChars(device_location, kRootPath, &storage_name_);
+  base::RemoveChars(device_location, kRootPath, &storage_name_);
   DCHECK(!storage_name_.empty());
 }
 
 MTPDeviceDelegateImplLinux::~MTPDeviceDelegateImplLinux() {
-  DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
+  DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
 }
 
 void MTPDeviceDelegateImplLinux::GetFileInfo(
     const base::FilePath& file_path,
     const GetFileInfoSuccessCallback& success_callback,
     const ErrorCallback& error_callback) {
-  DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
+  DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
   DCHECK(!file_path.empty());
-  base::Closure call_closure =
-      base::Bind(&GetFileInfoOnUIThread,
-                 storage_name_,
-                 GetDeviceRelativePath(device_path_, file_path),
-                 base::Bind(&MTPDeviceDelegateImplLinux::OnDidGetFileInfo,
-                            weak_ptr_factory_.GetWeakPtr(),
-                            success_callback),
-                 base::Bind(&MTPDeviceDelegateImplLinux::HandleDeviceFileError,
-                            weak_ptr_factory_.GetWeakPtr(),
-                            error_callback));
-  EnsureInitAndRunTask(PendingTaskInfo(FROM_HERE, call_closure));
+
+  // If a ReadDirectory operation is in progress, the file info may already be
+  // cached.
+  FileInfoCache::const_iterator it = file_info_cache_.find(file_path);
+  if (it != file_info_cache_.end()) {
+    // TODO(thestig): This code is repeated in several places. Combine them.
+    // e.g. c/b/media_galleries/win/mtp_device_operations_util.cc
+    const fileapi::DirectoryEntry& cached_file_entry = it->second;
+    base::File::Info info;
+    info.size = cached_file_entry.size;
+    info.is_directory = cached_file_entry.is_directory;
+    info.is_symbolic_link = false;
+    info.last_modified = cached_file_entry.last_modified_time;
+    info.creation_time = base::Time();
+
+    success_callback.Run(info);
+    return;
+  }
+  base::Closure closure =
+      base::Bind(&MTPDeviceDelegateImplLinux::GetFileInfoInternal,
+                 weak_ptr_factory_.GetWeakPtr(),
+                 file_path,
+                 success_callback,
+                 error_callback);
+  EnsureInitAndRunTask(PendingTaskInfo(file_path,
+                                       content::BrowserThread::IO,
+                                       FROM_HERE,
+                                       closure));
 }
 
 void MTPDeviceDelegateImplLinux::ReadDirectory(
     const base::FilePath& root,
     const ReadDirectorySuccessCallback& success_callback,
     const ErrorCallback& error_callback) {
-  DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
+  DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
   DCHECK(!root.empty());
-  std::string device_file_relative_path = GetDeviceRelativePath(device_path_,
-                                                                root);
-  base::Closure call_closure =
-      base::Bind(
-          &GetFileInfoOnUIThread,
-          storage_name_,
-          device_file_relative_path,
-          base::Bind(
-              &MTPDeviceDelegateImplLinux::OnDidGetFileInfoToReadDirectory,
-              weak_ptr_factory_.GetWeakPtr(),
-              device_file_relative_path,
-              success_callback,
-              error_callback),
-          base::Bind(&MTPDeviceDelegateImplLinux::HandleDeviceFileError,
-                     weak_ptr_factory_.GetWeakPtr(),
-                     error_callback));
-  EnsureInitAndRunTask(PendingTaskInfo(FROM_HERE, call_closure));
+  base::Closure closure =
+      base::Bind(&MTPDeviceDelegateImplLinux::ReadDirectoryInternal,
+                 weak_ptr_factory_.GetWeakPtr(),
+                 root,
+                 success_callback,
+                 error_callback);
+  EnsureInitAndRunTask(PendingTaskInfo(root,
+                                       content::BrowserThread::IO,
+                                       FROM_HERE,
+                                       closure));
 }
 
 void MTPDeviceDelegateImplLinux::CreateSnapshotFile(
     const base::FilePath& device_file_path,
-    const base::FilePath& snapshot_file_path,
+    const base::FilePath& local_path,
     const CreateSnapshotFileSuccessCallback& success_callback,
     const ErrorCallback& error_callback) {
-  DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
+  DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
   DCHECK(!device_file_path.empty());
-  DCHECK(!snapshot_file_path.empty());
-  std::string device_file_relative_path =
-      GetDeviceRelativePath(device_path_, device_file_path);
-  scoped_ptr<SnapshotRequestInfo> request_info(
-      new SnapshotRequestInfo(device_file_relative_path,
-                              snapshot_file_path,
-                              success_callback,
-                              error_callback));
-  base::Closure call_closure =
-      base::Bind(
-          &GetFileInfoOnUIThread,
-          storage_name_,
-          device_file_relative_path,
-          base::Bind(
-              &MTPDeviceDelegateImplLinux::OnDidGetFileInfoToCreateSnapshotFile,
-              weak_ptr_factory_.GetWeakPtr(),
-              base::Passed(&request_info)),
-          base::Bind(&MTPDeviceDelegateImplLinux::HandleDeviceFileError,
-                     weak_ptr_factory_.GetWeakPtr(),
-                     error_callback));
-  EnsureInitAndRunTask(PendingTaskInfo(FROM_HERE, call_closure));
+  DCHECK(!local_path.empty());
+  base::Closure closure =
+      base::Bind(&MTPDeviceDelegateImplLinux::CreateSnapshotFileInternal,
+                 weak_ptr_factory_.GetWeakPtr(),
+                 device_file_path,
+                 local_path,
+                 success_callback,
+                 error_callback);
+  EnsureInitAndRunTask(PendingTaskInfo(device_file_path,
+                                       content::BrowserThread::IO,
+                                       FROM_HERE,
+                                       closure));
+}
+
+bool MTPDeviceDelegateImplLinux::IsStreaming() {
+  return true;
+}
+
+void MTPDeviceDelegateImplLinux::ReadBytes(
+    const base::FilePath& device_file_path,
+    net::IOBuffer* buf, int64 offset, int buf_len,
+    const ReadBytesSuccessCallback& success_callback,
+    const ErrorCallback& error_callback) {
+  DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
+  DCHECK(!device_file_path.empty());
+  base::Closure closure =
+      base::Bind(&MTPDeviceDelegateImplLinux::ReadBytesInternal,
+                 weak_ptr_factory_.GetWeakPtr(),
+                 device_file_path,
+                 make_scoped_refptr(buf),
+                 offset,
+                 buf_len,
+                 success_callback,
+                 error_callback);
+  EnsureInitAndRunTask(PendingTaskInfo(device_file_path,
+                                       content::BrowserThread::IO,
+                                       FROM_HERE,
+                                       closure));
 }
 
 void MTPDeviceDelegateImplLinux::CancelPendingTasksAndDeleteDelegate() {
-  DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
+  DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
   // To cancel all the pending tasks, destroy the MTPDeviceTaskHelper object.
   content::BrowserThread::PostTask(
       content::BrowserThread::UI,
@@ -270,17 +440,159 @@ void MTPDeviceDelegateImplLinux::CancelPendingTasksAndDeleteDelegate() {
   delete this;
 }
 
+void MTPDeviceDelegateImplLinux::GetFileInfoInternal(
+    const base::FilePath& file_path,
+    const GetFileInfoSuccessCallback& success_callback,
+    const ErrorCallback& error_callback) {
+  DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
+
+  uint32 file_id;
+  if (CachedPathToId(file_path, &file_id)) {
+    GetFileInfoSuccessCallback success_callback_wrapper =
+        base::Bind(&MTPDeviceDelegateImplLinux::OnDidGetFileInfo,
+                   weak_ptr_factory_.GetWeakPtr(),
+                   success_callback);
+    ErrorCallback error_callback_wrapper =
+        base::Bind(&MTPDeviceDelegateImplLinux::HandleDeviceFileError,
+                   weak_ptr_factory_.GetWeakPtr(),
+                   error_callback,
+                   file_id);
+
+
+    base::Closure closure = base::Bind(&GetFileInfoOnUIThread,
+                                       storage_name_,
+                                       file_id,
+                                       success_callback_wrapper,
+                                       error_callback_wrapper);
+    EnsureInitAndRunTask(PendingTaskInfo(base::FilePath(),
+                                         content::BrowserThread::UI,
+                                         FROM_HERE,
+                                         closure));
+  } else {
+    error_callback.Run(base::File::FILE_ERROR_NOT_FOUND);
+  }
+  PendingRequestDone();
+}
+
+void MTPDeviceDelegateImplLinux::ReadDirectoryInternal(
+    const base::FilePath& root,
+    const ReadDirectorySuccessCallback& success_callback,
+    const ErrorCallback& error_callback) {
+  DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
+
+  uint32 dir_id;
+  if (CachedPathToId(root, &dir_id)) {
+    GetFileInfoSuccessCallback success_callback_wrapper =
+        base::Bind(&MTPDeviceDelegateImplLinux::OnDidGetFileInfoToReadDirectory,
+                   weak_ptr_factory_.GetWeakPtr(),
+                   dir_id,
+                   success_callback,
+                   error_callback);
+    ErrorCallback error_callback_wrapper =
+        base::Bind(&MTPDeviceDelegateImplLinux::HandleDeviceFileError,
+                   weak_ptr_factory_.GetWeakPtr(),
+                   error_callback,
+                   dir_id);
+    base::Closure closure = base::Bind(&GetFileInfoOnUIThread,
+                                       storage_name_,
+                                       dir_id,
+                                       success_callback_wrapper,
+                                       error_callback_wrapper);
+    EnsureInitAndRunTask(PendingTaskInfo(base::FilePath(),
+                                         content::BrowserThread::UI,
+                                         FROM_HERE,
+                                         closure));
+  } else {
+    error_callback.Run(base::File::FILE_ERROR_NOT_FOUND);
+  }
+  PendingRequestDone();
+}
+
+void MTPDeviceDelegateImplLinux::CreateSnapshotFileInternal(
+    const base::FilePath& device_file_path,
+    const base::FilePath& local_path,
+    const CreateSnapshotFileSuccessCallback& success_callback,
+    const ErrorCallback& error_callback) {
+  DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
+
+  uint32 file_id;
+  if (CachedPathToId(device_file_path, &file_id)) {
+    scoped_ptr<SnapshotRequestInfo> request_info(
+        new SnapshotRequestInfo(file_id,
+                                local_path,
+                                success_callback,
+                                error_callback));
+    GetFileInfoSuccessCallback success_callback_wrapper =
+        base::Bind(
+            &MTPDeviceDelegateImplLinux::OnDidGetFileInfoToCreateSnapshotFile,
+            weak_ptr_factory_.GetWeakPtr(),
+            base::Passed(&request_info));
+    ErrorCallback error_callback_wrapper =
+        base::Bind(&MTPDeviceDelegateImplLinux::HandleDeviceFileError,
+                   weak_ptr_factory_.GetWeakPtr(),
+                   error_callback,
+                   file_id);
+    base::Closure closure = base::Bind(&GetFileInfoOnUIThread,
+                                       storage_name_,
+                                       file_id,
+                                       success_callback_wrapper,
+                                       error_callback_wrapper);
+    EnsureInitAndRunTask(PendingTaskInfo(base::FilePath(),
+                                         content::BrowserThread::UI,
+                                         FROM_HERE,
+                                         closure));
+  } else {
+    error_callback.Run(base::File::FILE_ERROR_NOT_FOUND);
+  }
+  PendingRequestDone();
+}
+
+void MTPDeviceDelegateImplLinux::ReadBytesInternal(
+    const base::FilePath& device_file_path,
+    net::IOBuffer* buf, int64 offset, int buf_len,
+    const ReadBytesSuccessCallback& success_callback,
+    const ErrorCallback& error_callback) {
+  DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
+
+  uint32 file_id;
+  if (CachedPathToId(device_file_path, &file_id)) {
+    ReadBytesRequest request(
+        file_id, buf, offset, buf_len,
+        base::Bind(&MTPDeviceDelegateImplLinux::OnDidReadBytes,
+                   weak_ptr_factory_.GetWeakPtr(),
+                   success_callback),
+        base::Bind(&MTPDeviceDelegateImplLinux::HandleDeviceFileError,
+                   weak_ptr_factory_.GetWeakPtr(),
+                   error_callback,
+                   file_id));
+
+    base::Closure closure =
+        base::Bind(base::Bind(&ReadBytesOnUIThread, storage_name_, request));
+    EnsureInitAndRunTask(PendingTaskInfo(base::FilePath(),
+                                         content::BrowserThread::UI,
+                                         FROM_HERE,
+                                         closure));
+  } else {
+    error_callback.Run(base::File::FILE_ERROR_NOT_FOUND);
+  }
+  PendingRequestDone();
+}
+
 void MTPDeviceDelegateImplLinux::EnsureInitAndRunTask(
     const PendingTaskInfo& task_info) {
-  DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
+  DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
   if ((init_state_ == INITIALIZED) && !task_in_progress_) {
-    task_in_progress_ = true;
-    content::BrowserThread::PostTask(content::BrowserThread::UI,
-                                     task_info.location,
-                                     task_info.task);
+    RunTask(task_info);
     return;
   }
-  pending_tasks_.push(task_info);
+
+  // Only *Internal functions have empty paths. Since they are the continuation
+  // of the current running task, they get to cut in line.
+  if (task_info.path.empty())
+    pending_tasks_.push_front(task_info);
+  else
+    pending_tasks_.push_back(task_info);
+
   if (init_state_ == UNINITIALIZED) {
     init_state_ = PENDING_INIT;
     task_in_progress_ = true;
@@ -294,14 +606,37 @@ void MTPDeviceDelegateImplLinux::EnsureInitAndRunTask(
   }
 }
 
+void MTPDeviceDelegateImplLinux::RunTask(const PendingTaskInfo& task_info) {
+  DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
+  DCHECK_EQ(INITIALIZED, init_state_);
+  DCHECK(!task_in_progress_);
+  task_in_progress_ = true;
+
+  bool need_to_check_cache = !task_info.path.empty();
+  if (need_to_check_cache) {
+    base::FilePath uncached_path =
+        NextUncachedPathComponent(task_info.path, task_info.cached_path);
+    if (!uncached_path.empty()) {
+      // Save the current task and do a cache lookup first.
+      pending_tasks_.push_front(task_info);
+      FillFileCache(uncached_path);
+      return;
+    }
+  }
+
+  content::BrowserThread::PostTask(task_info.thread_id,
+                          task_info.location,
+                          task_info.task);
+}
+
 void MTPDeviceDelegateImplLinux::WriteDataIntoSnapshotFile(
-    const base::PlatformFileInfo& file_info) {
-  DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
+    const base::File::Info& file_info) {
+  DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
   DCHECK(current_snapshot_request_info_.get());
   DCHECK_GT(file_info.size, 0);
-  task_in_progress_ = true;
+  DCHECK(task_in_progress_);
   SnapshotRequestInfo request_info(
-      current_snapshot_request_info_->device_file_path,
+      current_snapshot_request_info_->file_id,
       current_snapshot_request_info_->snapshot_file_path,
       base::Bind(
           &MTPDeviceDelegateImplLinux::OnDidWriteDataIntoSnapshotFile,
@@ -319,58 +654,62 @@ void MTPDeviceDelegateImplLinux::WriteDataIntoSnapshotFile(
                                    task_closure);
 }
 
+void MTPDeviceDelegateImplLinux::PendingRequestDone() {
+  DCHECK(task_in_progress_);
+  task_in_progress_ = false;
+  ProcessNextPendingRequest();
+}
+
 void MTPDeviceDelegateImplLinux::ProcessNextPendingRequest() {
-  DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
+  DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
   DCHECK(!task_in_progress_);
   if (pending_tasks_.empty())
     return;
 
-  task_in_progress_ = true;
-  const PendingTaskInfo& task_info = pending_tasks_.front();
-  content::BrowserThread::PostTask(content::BrowserThread::UI,
-                                   task_info.location,
-                                   task_info.task);
-  pending_tasks_.pop();
+  PendingTaskInfo task_info = pending_tasks_.front();
+  pending_tasks_.pop_front();
+  RunTask(task_info);
 }
 
 void MTPDeviceDelegateImplLinux::OnInitCompleted(bool succeeded) {
-  DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
+  DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
   init_state_ = succeeded ? INITIALIZED : UNINITIALIZED;
-  task_in_progress_ = false;
-  ProcessNextPendingRequest();
+  PendingRequestDone();
 }
 
 void MTPDeviceDelegateImplLinux::OnDidGetFileInfo(
     const GetFileInfoSuccessCallback& success_callback,
-    const base::PlatformFileInfo& file_info) {
-  DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
+    const base::File::Info& file_info) {
+  DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
   success_callback.Run(file_info);
-  task_in_progress_ = false;
-  ProcessNextPendingRequest();
+  PendingRequestDone();
 }
 
 void MTPDeviceDelegateImplLinux::OnDidGetFileInfoToReadDirectory(
-    const std::string& root,
+    uint32 dir_id,
     const ReadDirectorySuccessCallback& success_callback,
     const ErrorCallback& error_callback,
-    const base::PlatformFileInfo& file_info) {
-  DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
+    const base::File::Info& file_info) {
+  DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
   DCHECK(task_in_progress_);
   if (!file_info.is_directory) {
     return HandleDeviceFileError(error_callback,
-                                 base::PLATFORM_FILE_ERROR_NOT_A_DIRECTORY);
+                                 dir_id,
+                                 base::File::FILE_ERROR_NOT_A_DIRECTORY);
   }
 
   base::Closure task_closure =
       base::Bind(&ReadDirectoryOnUIThread,
                  storage_name_,
-                 root,
+                 dir_id,
                  base::Bind(&MTPDeviceDelegateImplLinux::OnDidReadDirectory,
                             weak_ptr_factory_.GetWeakPtr(),
+                            dir_id,
                             success_callback),
                  base::Bind(&MTPDeviceDelegateImplLinux::HandleDeviceFileError,
                             weak_ptr_factory_.GetWeakPtr(),
-                            error_callback));
+                            error_callback,
+                            dir_id));
   content::BrowserThread::PostTask(content::BrowserThread::UI,
                                    FROM_HERE,
                                    task_closure);
@@ -378,21 +717,23 @@ void MTPDeviceDelegateImplLinux::OnDidGetFileInfoToReadDirectory(
 
 void MTPDeviceDelegateImplLinux::OnDidGetFileInfoToCreateSnapshotFile(
     scoped_ptr<SnapshotRequestInfo> snapshot_request_info,
-    const base::PlatformFileInfo& file_info) {
-  DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
+    const base::File::Info& file_info) {
+  DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
   DCHECK(!current_snapshot_request_info_.get());
   DCHECK(snapshot_request_info.get());
   DCHECK(task_in_progress_);
-  base::PlatformFileError error = base::PLATFORM_FILE_OK;
+  base::File::Error error = base::File::FILE_OK;
   if (file_info.is_directory)
-    error = base::PLATFORM_FILE_ERROR_NOT_A_FILE;
+    error = base::File::FILE_ERROR_NOT_A_FILE;
   else if (file_info.size < 0 || file_info.size > kuint32max)
-    error = base::PLATFORM_FILE_ERROR_FAILED;
+    error = base::File::FILE_ERROR_FAILED;
 
-  if (error != base::PLATFORM_FILE_OK)
-    return HandleDeviceFileError(snapshot_request_info->error_callback, error);
+  if (error != base::File::FILE_OK)
+    return HandleDeviceFileError(snapshot_request_info->error_callback,
+                                 snapshot_request_info->file_id,
+                                 error);
 
-  base::PlatformFileInfo snapshot_file_info(file_info);
+  base::File::Info snapshot_file_info(file_info);
   // Modify the last modified time to null. This prevents the time stamp
   // verfication in LocalFileStreamReader.
   snapshot_file_info.last_modified = base::Time();
@@ -407,50 +748,200 @@ void MTPDeviceDelegateImplLinux::OnDidGetFileInfoToCreateSnapshotFile(
 }
 
 void MTPDeviceDelegateImplLinux::OnDidReadDirectory(
+    uint32 dir_id,
     const ReadDirectorySuccessCallback& success_callback,
-    const fileapi::AsyncFileUtil::EntryList& file_list) {
-  DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
-  success_callback.Run(file_list, false /*no more entries*/);
-  task_in_progress_ = false;
-  ProcessNextPendingRequest();
+    const fileapi::AsyncFileUtil::EntryList& file_list,
+    bool has_more) {
+  DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
+
+  FileIdToMTPFileNodeMap::iterator it = file_id_to_node_map_.find(dir_id);
+  DCHECK(it != file_id_to_node_map_.end());
+  MTPFileNode* dir_node = it->second;
+
+  // Traverse the MTPFileNode tree to reconstuct the full path for |dir_id|.
+  std::deque<std::string> dir_path_parts;
+  MTPFileNode* parent_node = dir_node;
+  while (parent_node->parent()) {
+    dir_path_parts.push_front(parent_node->file_name());
+    parent_node = parent_node->parent();
+  }
+  base::FilePath dir_path = device_path_;
+  for (size_t i = 0; i < dir_path_parts.size(); ++i)
+    dir_path = dir_path.Append(dir_path_parts[i]);
+
+  fileapi::AsyncFileUtil::EntryList normalized_file_list;
+  for (size_t i = 0; i < file_list.size(); ++i) {
+    normalized_file_list.push_back(file_list[i]);
+    fileapi::DirectoryEntry& entry = normalized_file_list.back();
+
+    // |entry.name| has the file id encoded in it. Decode here.
+    size_t separator_idx = entry.name.find_last_of(',');
+    DCHECK_NE(std::string::npos, separator_idx);
+    std::string file_id_str = entry.name.substr(separator_idx);
+    file_id_str = file_id_str.substr(1);  // Get rid of the comma.
+    uint32 file_id = 0;
+    bool ret = base::StringToUint(file_id_str, &file_id);
+    DCHECK(ret);
+    entry.name = entry.name.substr(0, separator_idx);
+
+    // Refresh the in memory tree.
+    dir_node->EnsureChildExists(entry.name, file_id);
+    child_nodes_seen_.insert(entry.name);
+
+    // Add to |file_info_cache_|.
+    file_info_cache_[dir_path.Append(entry.name)] = entry;
+  }
+
+  success_callback.Run(normalized_file_list, has_more);
+  if (has_more)
+    return;  // Wait to be called again.
+
+  // Last call, finish book keeping and continue with the next request.
+  dir_node->ClearNonexistentChildren(child_nodes_seen_);
+  child_nodes_seen_.clear();
+  file_info_cache_.clear();
+
+  PendingRequestDone();
 }
 
 void MTPDeviceDelegateImplLinux::OnDidWriteDataIntoSnapshotFile(
-    const base::PlatformFileInfo& file_info,
+    const base::File::Info& file_info,
     const base::FilePath& snapshot_file_path) {
-  DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
+  DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
   DCHECK(current_snapshot_request_info_.get());
-  DCHECK(task_in_progress_);
   current_snapshot_request_info_->success_callback.Run(
       file_info, snapshot_file_path);
-  task_in_progress_ = false;
   current_snapshot_request_info_.reset();
-  ProcessNextPendingRequest();
+  PendingRequestDone();
 }
 
 void MTPDeviceDelegateImplLinux::OnWriteDataIntoSnapshotFileError(
-    base::PlatformFileError error) {
-  DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
+    base::File::Error error) {
+  DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
   DCHECK(current_snapshot_request_info_.get());
-  DCHECK(task_in_progress_);
   current_snapshot_request_info_->error_callback.Run(error);
-  task_in_progress_ = false;
   current_snapshot_request_info_.reset();
-  ProcessNextPendingRequest();
+  PendingRequestDone();
+}
+
+void MTPDeviceDelegateImplLinux::OnDidReadBytes(
+    const ReadBytesSuccessCallback& success_callback,
+    const base::File::Info& file_info, int bytes_read) {
+  DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
+  success_callback.Run(file_info, bytes_read);
+  PendingRequestDone();
+}
+
+void MTPDeviceDelegateImplLinux::OnDidFillFileCache(
+    const base::FilePath& path,
+    const fileapi::AsyncFileUtil::EntryList& /* file_list */,
+    bool has_more) {
+  DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
+  DCHECK(path.IsParent(pending_tasks_.front().path));
+  if (has_more)
+    return;  // Wait until all entries have been read.
+  pending_tasks_.front().cached_path = path;
+}
+
+void MTPDeviceDelegateImplLinux::OnFillFileCacheFailed(
+    base::File::Error /* error */) {
+  DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
+  // When filling the cache fails for the task at the front of the queue, clear
+  // the path of the task so it will not try to do any more caching. Instead,
+  // the task will just run and fail the CachedPathToId() lookup.
+  pending_tasks_.front().path.clear();
 }
 
 void MTPDeviceDelegateImplLinux::HandleDeviceFileError(
     const ErrorCallback& error_callback,
-    base::PlatformFileError error) {
-  DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
+    uint32 file_id,
+    base::File::Error error) {
+  DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
+
+  FileIdToMTPFileNodeMap::iterator it = file_id_to_node_map_.find(file_id);
+  if (it != file_id_to_node_map_.end()) {
+    MTPFileNode* parent = it->second->parent();
+    if (parent) {
+      bool ret = parent->DeleteChild(file_id);
+      DCHECK(ret);
+    }
+  }
   error_callback.Run(error);
-  task_in_progress_ = false;
-  ProcessNextPendingRequest();
+  PendingRequestDone();
+}
+
+base::FilePath MTPDeviceDelegateImplLinux::NextUncachedPathComponent(
+    const base::FilePath& path,
+    const base::FilePath& cached_path) const {
+  DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
+  DCHECK(cached_path.empty() || cached_path.IsParent(path));
+
+  base::FilePath uncached_path;
+  std::string device_relpath = GetDeviceRelativePath(device_path_, path);
+  if (!device_relpath.empty() && device_relpath != kRootPath) {
+    uncached_path = device_path_;
+    std::vector<std::string> device_relpath_components;
+    base::SplitString(device_relpath, '/', &device_relpath_components);
+    DCHECK(!device_relpath_components.empty());
+    bool all_components_cached = true;
+    const MTPFileNode* current_node = root_node_.get();
+    for (size_t i = 0; i < device_relpath_components.size(); ++i) {
+      current_node = current_node->GetChild(device_relpath_components[i]);
+      if (!current_node) {
+        // With a cache miss, check if it is a genuine failure. If so, pretend
+        // the entire |path| is cached, so there is no further attempt to do
+        // more caching. The actual operation will then fail.
+        all_components_cached =
+            !cached_path.empty() && (uncached_path == cached_path);
+        break;
+      }
+      uncached_path = uncached_path.Append(device_relpath_components[i]);
+    }
+    if (all_components_cached)
+      uncached_path.clear();
+  }
+  return uncached_path;
+}
+
+void MTPDeviceDelegateImplLinux::FillFileCache(
+    const base::FilePath& uncached_path) {
+  DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
+  DCHECK(task_in_progress_);
+
+  ReadDirectorySuccessCallback success_callback =
+      base::Bind(&MTPDeviceDelegateImplLinux::OnDidFillFileCache,
+                 weak_ptr_factory_.GetWeakPtr(),
+                 uncached_path);
+  ErrorCallback error_callback =
+      base::Bind(&MTPDeviceDelegateImplLinux::OnFillFileCacheFailed,
+                 weak_ptr_factory_.GetWeakPtr());
+  ReadDirectoryInternal(uncached_path, success_callback, error_callback);
+}
+
+
+bool MTPDeviceDelegateImplLinux::CachedPathToId(const base::FilePath& path,
+                                                uint32* id) const {
+  DCHECK(id);
+
+  std::string device_relpath = GetDeviceRelativePath(device_path_, path);
+  if (device_relpath.empty())
+    return false;
+  std::vector<std::string> device_relpath_components;
+  if (device_relpath != kRootPath)
+    base::SplitString(device_relpath, '/', &device_relpath_components);
+  const MTPFileNode* current_node = root_node_.get();
+  for (size_t i = 0; i < device_relpath_components.size(); ++i) {
+    current_node = current_node->GetChild(device_relpath_components[i]);
+    if (!current_node)
+      return false;
+  }
+  *id = current_node->file_id();
+  return true;
 }
 
 void CreateMTPDeviceAsyncDelegate(
     const std::string& device_location,
     const CreateMTPDeviceAsyncDelegateCallback& callback) {
-  DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
+  DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
   callback.Run(new MTPDeviceDelegateImplLinux(device_location));
 }