Add ICU message format support
[chromium-blink-merge.git] / chrome / browser / media_galleries / linux / mtp_device_delegate_impl_linux.cc
blob01f9a6e05905c8c3e7369efe7feb783b5417cbbf
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.
5 #include "chrome/browser/media_galleries/linux/mtp_device_delegate_impl_linux.h"
7 #include <fcntl.h>
8 #include <algorithm>
9 #include <vector>
11 #include "base/bind.h"
12 #include "base/files/file_util.h"
13 #include "base/numerics/safe_conversions.h"
14 #include "base/posix/eintr_wrapper.h"
15 #include "base/strings/string_number_conversions.h"
16 #include "base/strings/string_split.h"
17 #include "base/strings/string_util.h"
18 #include "chrome/browser/media_galleries/linux/mtp_device_task_helper.h"
19 #include "chrome/browser/media_galleries/linux/mtp_device_task_helper_map_service.h"
20 #include "chrome/browser/media_galleries/linux/snapshot_file_details.h"
21 #include "net/base/io_buffer.h"
22 #include "third_party/cros_system_api/dbus/service_constants.h"
24 namespace {
26 // File path separator constant.
27 const char kRootPath[] = "/";
29 // Returns the device relative file path given |file_path|.
30 // E.g.: If the |file_path| is "/usb:2,2:12345/DCIM" and |registered_dev_path|
31 // is "/usb:2,2:12345", this function returns the device relative path which is
32 // "DCIM".
33 // In the special case when |registered_dev_path| and |file_path| are the same,
34 // return |kRootPath|.
35 std::string GetDeviceRelativePath(const base::FilePath& registered_dev_path,
36 const base::FilePath& file_path) {
37 DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
38 DCHECK(!registered_dev_path.empty());
39 DCHECK(!file_path.empty());
40 std::string result;
41 if (registered_dev_path == file_path) {
42 result = kRootPath;
43 } else {
44 base::FilePath relative_path;
45 if (registered_dev_path.AppendRelativePath(file_path, &relative_path)) {
46 DCHECK(!relative_path.empty());
47 result = relative_path.value();
50 return result;
53 // Returns the MTPDeviceTaskHelper object associated with the MTP device
54 // storage.
56 // |storage_name| specifies the name of the storage device.
57 // |read_only| specifies the mode of the storage device.
58 // Returns NULL if the |storage_name| is no longer valid (e.g. because the
59 // corresponding storage device is detached, etc).
60 MTPDeviceTaskHelper* GetDeviceTaskHelperForStorage(
61 const std::string& storage_name,
62 const bool read_only) {
63 DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
64 return MTPDeviceTaskHelperMapService::GetInstance()->GetDeviceTaskHelper(
65 storage_name,
66 read_only);
69 // Opens the storage device for communication.
71 // Called on the UI thread to dispatch the request to the
72 // MediaTransferProtocolManager.
74 // |storage_name| specifies the name of the storage device.
75 // |read_only| specifies the mode of the storage device.
76 // |reply_callback| is called when the OpenStorage request completes.
77 // |reply_callback| runs on the IO thread.
78 void OpenStorageOnUIThread(
79 const std::string& storage_name,
80 const bool read_only,
81 const MTPDeviceTaskHelper::OpenStorageCallback& reply_callback) {
82 DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
83 MTPDeviceTaskHelper* task_helper =
84 GetDeviceTaskHelperForStorage(storage_name, read_only);
85 if (!task_helper) {
86 task_helper =
87 MTPDeviceTaskHelperMapService::GetInstance()->CreateDeviceTaskHelper(
88 storage_name, read_only);
90 task_helper->OpenStorage(storage_name, read_only, reply_callback);
93 // Creates |directory_name| on |parent_id|.
95 // |storage_name| specifies the name of the storage device.
96 // |read_only| specifies the mode of the storage device.
97 // |parent_id| is an object id of the parent directory.
98 // |directory_name| is name of the new directory.
99 // |success_callback| is called when the directory is created successfully.
100 // |error_callback| is called when it fails to create a directory.
101 // |success_callback| and |error_callback| runs on the IO thread.
102 void CreateDirectoryOnUIThread(
103 const std::string& storage_name,
104 const bool read_only,
105 const uint32 parent_id,
106 const std::string& directory_name,
107 const MTPDeviceTaskHelper::CreateDirectorySuccessCallback& success_callback,
108 const MTPDeviceTaskHelper::ErrorCallback& error_callback) {
109 DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
110 MTPDeviceTaskHelper* task_helper =
111 GetDeviceTaskHelperForStorage(storage_name, read_only);
112 if (!task_helper)
113 return;
114 task_helper->CreateDirectory(parent_id, directory_name, success_callback,
115 error_callback);
118 // Enumerates the |dir_id| directory file entries.
120 // Called on the UI thread to dispatch the request to the
121 // MediaTransferProtocolManager.
123 // |storage_name| specifies the name of the storage device.
124 // |read_only| specifies the mode of the storage device.
125 // |directory_id| is an id of a directory to read.
126 // |max_size| is a maximum size to read. Set 0 not to specify the maximum size.
127 // |success_callback| is called when the ReadDirectory request succeeds.
128 // |error_callback| is called when the ReadDirectory request fails.
129 // |success_callback| and |error_callback| runs on the IO thread.
130 void ReadDirectoryOnUIThread(
131 const std::string& storage_name,
132 const bool read_only,
133 const uint32 directory_id,
134 const size_t max_size,
135 const MTPDeviceTaskHelper::ReadDirectorySuccessCallback& success_callback,
136 const MTPDeviceTaskHelper::ErrorCallback& error_callback) {
137 DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
138 MTPDeviceTaskHelper* task_helper =
139 GetDeviceTaskHelperForStorage(storage_name, read_only);
140 if (!task_helper)
141 return;
142 task_helper->ReadDirectory(directory_id, max_size, success_callback,
143 error_callback);
146 // Gets the |file_path| details.
148 // Called on the UI thread to dispatch the request to the
149 // MediaTransferProtocolManager.
151 // |storage_name| specifies the name of the storage device.
152 // |read_only| specifies the mode of the storage device.
153 // |success_callback| is called when the GetFileInfo request succeeds.
154 // |error_callback| is called when the GetFileInfo request fails.
155 // |success_callback| and |error_callback| runs on the IO thread.
156 void GetFileInfoOnUIThread(
157 const std::string& storage_name,
158 const bool read_only,
159 uint32 file_id,
160 const MTPDeviceTaskHelper::GetFileInfoSuccessCallback& success_callback,
161 const MTPDeviceTaskHelper::ErrorCallback& error_callback) {
162 DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
163 MTPDeviceTaskHelper* task_helper =
164 GetDeviceTaskHelperForStorage(storage_name, read_only);
165 if (!task_helper)
166 return;
167 task_helper->GetFileInfo(file_id, success_callback, error_callback);
170 // Copies the contents of |device_file_path| to |snapshot_file_path|.
172 // Called on the UI thread to dispatch the request to the
173 // MediaTransferProtocolManager.
175 // |storage_name| specifies the name of the storage device.
176 // |read_only| specifies the mode of the storage device.
177 // |device_file_path| specifies the media device file path.
178 // |snapshot_file_path| specifies the platform path of the snapshot file.
179 // |file_size| specifies the number of bytes that will be written to the
180 // snapshot file.
181 // |success_callback| is called when the copy operation succeeds.
182 // |error_callback| is called when the copy operation fails.
183 // |success_callback| and |error_callback| runs on the IO thread.
184 void WriteDataIntoSnapshotFileOnUIThread(
185 const std::string& storage_name,
186 const bool read_only,
187 const SnapshotRequestInfo& request_info,
188 const base::File::Info& snapshot_file_info) {
189 DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
190 MTPDeviceTaskHelper* task_helper =
191 GetDeviceTaskHelperForStorage(storage_name, read_only);
192 if (!task_helper)
193 return;
194 task_helper->WriteDataIntoSnapshotFile(request_info, snapshot_file_info);
197 // Copies the contents of |device_file_path| to |snapshot_file_path|.
199 // Called on the UI thread to dispatch the request to the
200 // MediaTransferProtocolManager.
202 // |storage_name| specifies the name of the storage device.
203 // |read_only| specifies the mode of the storage device.
204 // |request| is a struct containing details about the byte read request.
205 void ReadBytesOnUIThread(
206 const std::string& storage_name,
207 const bool read_only,
208 const MTPDeviceAsyncDelegate::ReadBytesRequest& request) {
209 DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
210 MTPDeviceTaskHelper* task_helper =
211 GetDeviceTaskHelperForStorage(storage_name, read_only);
212 if (!task_helper)
213 return;
214 task_helper->ReadBytes(request);
217 // Renames |object_id| to |new_name|.
219 // |storage_name| specifies the name of the storage device.
220 // |read_only| specifies the mode of the storage device.
221 // |object_id| is an id of object to be renamed.
222 // |new_name| is new name of the object.
223 // |success_callback| is called when the object is renamed successfully.
224 // |error_callback| is called when it fails to rename the object.
225 // |success_callback| and |error_callback| runs on the IO thread.
226 void RenameObjectOnUIThread(
227 const std::string& storage_name,
228 const bool read_only,
229 const uint32 object_id,
230 const std::string& new_name,
231 const MTPDeviceTaskHelper::RenameObjectSuccessCallback& success_callback,
232 const MTPDeviceTaskHelper::ErrorCallback& error_callback) {
233 DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
234 MTPDeviceTaskHelper* task_helper =
235 GetDeviceTaskHelperForStorage(storage_name, read_only);
236 if (!task_helper)
237 return;
238 task_helper->RenameObject(object_id, new_name, success_callback,
239 error_callback);
242 // Copies the file |source_file_descriptor| to |file_name| in |parent_id|.
244 // |storage_name| specifies the name of the storage device.
245 // |read_only| specifies the mode of the storage device.
246 // |source_file_descriptor| file descriptor of source file.
247 // |parent_id| object id of a target directory.
248 // |file_name| file name of a target file.
249 // |success_callback| is called when the file is copied successfully.
250 // |error_callback| is called when it fails to copy file.
251 // Since this method does not close the file descriptor, callbacks are
252 // responsible for closing it.
253 void CopyFileFromLocalOnUIThread(
254 const std::string& storage_name,
255 const bool read_only,
256 const int source_file_descriptor,
257 const uint32 parent_id,
258 const std::string& file_name,
259 const MTPDeviceTaskHelper::CopyFileFromLocalSuccessCallback&
260 success_callback,
261 const MTPDeviceTaskHelper::ErrorCallback& error_callback) {
262 DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
263 MTPDeviceTaskHelper* task_helper =
264 GetDeviceTaskHelperForStorage(storage_name, read_only);
265 if (!task_helper)
266 return;
267 task_helper->CopyFileFromLocal(storage_name, source_file_descriptor,
268 parent_id, file_name, success_callback,
269 error_callback);
272 // Deletes |object_id|.
274 // Called on the UI thread to dispatch the request to the
275 // MediaTransferProtocolManager.
277 // |storage_name| specifies the name of the storage device.
278 // |read_only| specifies the mode of the storage device.
279 // |object_id| is the object to be deleted.
280 // |success_callback| is called when the object is deleted successfully.
281 // |error_callback| is called when it fails to delete the object.
282 // |success_callback| and |error_callback| runs on the IO thread.
283 void DeleteObjectOnUIThread(
284 const std::string storage_name,
285 const bool read_only,
286 const uint32 object_id,
287 const MTPDeviceTaskHelper::DeleteObjectSuccessCallback success_callback,
288 const MTPDeviceTaskHelper::ErrorCallback error_callback) {
289 DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
290 MTPDeviceTaskHelper* task_helper =
291 GetDeviceTaskHelperForStorage(storage_name, read_only);
292 if (!task_helper)
293 return;
294 task_helper->DeleteObject(object_id, success_callback, error_callback);
297 // Closes the device storage specified by the |storage_name| and destroys the
298 // MTPDeviceTaskHelper object associated with the device storage.
300 // Called on the UI thread to dispatch the request to the
301 // MediaTransferProtocolManager.
302 void CloseStorageAndDestroyTaskHelperOnUIThread(
303 const std::string& storage_name,
304 const bool read_only) {
305 DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
306 MTPDeviceTaskHelper* task_helper =
307 GetDeviceTaskHelperForStorage(storage_name, read_only);
308 if (!task_helper)
309 return;
310 task_helper->CloseStorage();
311 MTPDeviceTaskHelperMapService::GetInstance()->DestroyDeviceTaskHelper(
312 storage_name, read_only);
315 // Opens |file_path| with |flags|. Returns the result as a pair.
316 // first is file descriptor.
317 // second is base::File::Error. This value is set as following.
318 // - When it succeeds to open a file descriptor, base::File::FILE_OK is set.
319 // - When |file_path| is a directory, base::File::FILE_ERROR_NOT_A_FILE is set.
320 // - When |file_path| does not exist, base::File::FILE_ERROR_NOT_FOUND is set.
321 // - For other error cases, base::File::FILE_ERROR_FAILED is set.
322 std::pair<int, base::File::Error> OpenFileDescriptor(const char* file_path,
323 const int flags) {
324 DCHECK_CURRENTLY_ON(content::BrowserThread::FILE);
326 if (base::DirectoryExists(base::FilePath(file_path)))
327 return std::make_pair(-1, base::File::FILE_ERROR_NOT_A_FILE);
328 int file_descriptor = open(file_path, flags);
329 if (file_descriptor >= 0)
330 return std::make_pair(file_descriptor, base::File::FILE_OK);
331 if (errno == ENOENT)
332 return std::make_pair(file_descriptor, base::File::FILE_ERROR_NOT_FOUND);
333 return std::make_pair(file_descriptor, base::File::FILE_ERROR_FAILED);
336 // Closes |file_descriptor| on file thread.
337 void CloseFileDescriptor(const int file_descriptor) {
338 DCHECK_CURRENTLY_ON(content::BrowserThread::FILE);
340 IGNORE_EINTR(close(file_descriptor));
343 // Deletes a temporary file |file_path|.
344 void DeleteTemporaryFile(const base::FilePath& file_path) {
345 content::BrowserThread::PostBlockingPoolTask(
346 FROM_HERE, base::Bind(base::IgnoreResult(base::DeleteFile), file_path,
347 false /* not recursive*/));
350 // A fake callback to be passed as CopyFileProgressCallback.
351 void FakeCopyFileProgressCallback(int64 size) {
354 } // namespace
356 MTPDeviceDelegateImplLinux::PendingTaskInfo::PendingTaskInfo(
357 const base::FilePath& path,
358 content::BrowserThread::ID thread_id,
359 const tracked_objects::Location& location,
360 const base::Closure& task)
361 : path(path),
362 thread_id(thread_id),
363 location(location),
364 task(task) {
367 MTPDeviceDelegateImplLinux::PendingTaskInfo::~PendingTaskInfo() {
370 // Represents a file on the MTP device.
371 // Lives on the IO thread.
372 class MTPDeviceDelegateImplLinux::MTPFileNode {
373 public:
374 MTPFileNode(uint32 file_id,
375 const std::string& file_name,
376 MTPFileNode* parent,
377 FileIdToMTPFileNodeMap* file_id_to_node_map);
378 ~MTPFileNode();
380 const MTPFileNode* GetChild(const std::string& name) const;
382 void EnsureChildExists(const std::string& name, uint32 id);
384 // Clears all the children, except those in |children_to_keep|.
385 void ClearNonexistentChildren(
386 const std::set<std::string>& children_to_keep);
388 bool DeleteChild(uint32 file_id);
390 bool HasChildren() const;
392 uint32 file_id() const { return file_id_; }
393 const std::string& file_name() const { return file_name_; }
394 MTPFileNode* parent() { return parent_; }
396 private:
397 // Container for holding a node's children.
398 typedef base::ScopedPtrHashMap<std::string, scoped_ptr<MTPFileNode>>
399 ChildNodes;
401 const uint32 file_id_;
402 const std::string file_name_;
404 ChildNodes children_;
405 MTPFileNode* const parent_;
406 FileIdToMTPFileNodeMap* file_id_to_node_map_;
408 DISALLOW_COPY_AND_ASSIGN(MTPFileNode);
411 MTPDeviceDelegateImplLinux::MTPFileNode::MTPFileNode(
412 uint32 file_id,
413 const std::string& file_name,
414 MTPFileNode* parent,
415 FileIdToMTPFileNodeMap* file_id_to_node_map)
416 : file_id_(file_id),
417 file_name_(file_name),
418 parent_(parent),
419 file_id_to_node_map_(file_id_to_node_map) {
420 DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
421 DCHECK(file_id_to_node_map_);
422 DCHECK(!ContainsKey(*file_id_to_node_map_, file_id_));
423 (*file_id_to_node_map_)[file_id_] = this;
426 MTPDeviceDelegateImplLinux::MTPFileNode::~MTPFileNode() {
427 DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
428 size_t erased = file_id_to_node_map_->erase(file_id_);
429 DCHECK_EQ(1U, erased);
432 const MTPDeviceDelegateImplLinux::MTPFileNode*
433 MTPDeviceDelegateImplLinux::MTPFileNode::GetChild(
434 const std::string& name) const {
435 DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
436 return children_.get(name);
439 void MTPDeviceDelegateImplLinux::MTPFileNode::EnsureChildExists(
440 const std::string& name,
441 uint32 id) {
442 DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
443 const MTPFileNode* child = GetChild(name);
444 if (child && child->file_id() == id)
445 return;
447 children_.set(
448 name,
449 make_scoped_ptr(new MTPFileNode(id, name, this, file_id_to_node_map_)));
452 void MTPDeviceDelegateImplLinux::MTPFileNode::ClearNonexistentChildren(
453 const std::set<std::string>& children_to_keep) {
454 DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
455 std::set<std::string> children_to_erase;
456 for (ChildNodes::const_iterator it = children_.begin();
457 it != children_.end(); ++it) {
458 if (ContainsKey(children_to_keep, it->first))
459 continue;
460 children_to_erase.insert(it->first);
462 for (std::set<std::string>::iterator it = children_to_erase.begin();
463 it != children_to_erase.end(); ++it) {
464 children_.take_and_erase(*it);
468 bool MTPDeviceDelegateImplLinux::MTPFileNode::DeleteChild(uint32 file_id) {
469 DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
470 for (ChildNodes::iterator it = children_.begin();
471 it != children_.end(); ++it) {
472 if (it->second->file_id() == file_id) {
473 DCHECK(!it->second->HasChildren());
474 children_.erase(it);
475 return true;
478 return false;
481 bool MTPDeviceDelegateImplLinux::MTPFileNode::HasChildren() const {
482 DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
483 return children_.size() > 0;
486 MTPDeviceDelegateImplLinux::MTPDeviceDelegateImplLinux(
487 const std::string& device_location,
488 const bool read_only)
489 : init_state_(UNINITIALIZED),
490 task_in_progress_(false),
491 device_path_(device_location),
492 read_only_(read_only),
493 root_node_(new MTPFileNode(mtpd::kRootFileId,
494 "", // Root node has no name.
495 NULL, // And no parent node.
496 &file_id_to_node_map_)),
497 weak_ptr_factory_(this) {
498 DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
499 DCHECK(!device_path_.empty());
500 base::RemoveChars(device_location, kRootPath, &storage_name_);
501 DCHECK(!storage_name_.empty());
504 MTPDeviceDelegateImplLinux::~MTPDeviceDelegateImplLinux() {
505 DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
508 void MTPDeviceDelegateImplLinux::CreateDirectory(
509 const base::FilePath& directory_path,
510 const bool exclusive,
511 const bool recursive,
512 const CreateDirectorySuccessCallback& success_callback,
513 const ErrorCallback& error_callback) {
514 DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
515 DCHECK(!directory_path.empty());
517 // If |directory_path| is not the path in this device, fails with error.
518 if (!device_path_.IsParent(directory_path)) {
519 error_callback.Run(base::File::FILE_ERROR_FAILED);
520 return;
523 // Decomposes |directory_path| to components. CreateDirectoryInternal creates
524 // directories by reading |components| from back.
525 std::vector<base::FilePath> components;
526 if (recursive) {
527 for (base::FilePath path = directory_path; path != device_path_;
528 path = path.DirName()) {
529 components.push_back(path);
531 } else {
532 components.push_back(directory_path);
535 const base::Closure closure =
536 base::Bind(&MTPDeviceDelegateImplLinux::CreateDirectoryInternal,
537 weak_ptr_factory_.GetWeakPtr(), components, exclusive,
538 success_callback, error_callback);
539 EnsureInitAndRunTask(PendingTaskInfo(
540 directory_path, content::BrowserThread::IO, FROM_HERE, closure));
543 void MTPDeviceDelegateImplLinux::GetFileInfo(
544 const base::FilePath& file_path,
545 const GetFileInfoSuccessCallback& success_callback,
546 const ErrorCallback& error_callback) {
547 DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
548 DCHECK(!file_path.empty());
550 // If a ReadDirectory operation is in progress, the file info may already be
551 // cached.
552 FileInfoCache::const_iterator it = file_info_cache_.find(file_path);
553 if (it != file_info_cache_.end()) {
554 // TODO(thestig): This code is repeated in several places. Combine them.
555 // e.g. c/b/media_galleries/win/mtp_device_operations_util.cc
556 const storage::DirectoryEntry& cached_file_entry = it->second;
557 base::File::Info info;
558 info.size = cached_file_entry.size;
559 info.is_directory = cached_file_entry.is_directory;
560 info.is_symbolic_link = false;
561 info.last_modified = cached_file_entry.last_modified_time;
562 info.creation_time = base::Time();
564 success_callback.Run(info);
565 return;
567 base::Closure closure =
568 base::Bind(&MTPDeviceDelegateImplLinux::GetFileInfoInternal,
569 weak_ptr_factory_.GetWeakPtr(),
570 file_path,
571 success_callback,
572 error_callback);
573 EnsureInitAndRunTask(PendingTaskInfo(file_path,
574 content::BrowserThread::IO,
575 FROM_HERE,
576 closure));
579 void MTPDeviceDelegateImplLinux::ReadDirectory(
580 const base::FilePath& root,
581 const ReadDirectorySuccessCallback& success_callback,
582 const ErrorCallback& error_callback) {
583 DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
584 DCHECK(!root.empty());
585 base::Closure closure =
586 base::Bind(&MTPDeviceDelegateImplLinux::ReadDirectoryInternal,
587 weak_ptr_factory_.GetWeakPtr(),
588 root,
589 success_callback,
590 error_callback);
591 EnsureInitAndRunTask(PendingTaskInfo(root,
592 content::BrowserThread::IO,
593 FROM_HERE,
594 closure));
597 void MTPDeviceDelegateImplLinux::CreateSnapshotFile(
598 const base::FilePath& device_file_path,
599 const base::FilePath& local_path,
600 const CreateSnapshotFileSuccessCallback& success_callback,
601 const ErrorCallback& error_callback) {
602 DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
603 DCHECK(!device_file_path.empty());
604 DCHECK(!local_path.empty());
605 base::Closure closure =
606 base::Bind(&MTPDeviceDelegateImplLinux::CreateSnapshotFileInternal,
607 weak_ptr_factory_.GetWeakPtr(),
608 device_file_path,
609 local_path,
610 success_callback,
611 error_callback);
612 EnsureInitAndRunTask(PendingTaskInfo(device_file_path,
613 content::BrowserThread::IO,
614 FROM_HERE,
615 closure));
618 bool MTPDeviceDelegateImplLinux::IsStreaming() {
619 return true;
622 void MTPDeviceDelegateImplLinux::ReadBytes(
623 const base::FilePath& device_file_path,
624 const scoped_refptr<net::IOBuffer>& buf,
625 int64 offset,
626 int buf_len,
627 const ReadBytesSuccessCallback& success_callback,
628 const ErrorCallback& error_callback) {
629 DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
630 DCHECK(!device_file_path.empty());
631 base::Closure closure =
632 base::Bind(&MTPDeviceDelegateImplLinux::ReadBytesInternal,
633 weak_ptr_factory_.GetWeakPtr(),
634 device_file_path,
635 buf,
636 offset,
637 buf_len,
638 success_callback,
639 error_callback);
640 EnsureInitAndRunTask(PendingTaskInfo(device_file_path,
641 content::BrowserThread::IO,
642 FROM_HERE,
643 closure));
646 bool MTPDeviceDelegateImplLinux::IsReadOnly() const {
647 return read_only_;
650 void MTPDeviceDelegateImplLinux::CopyFileLocal(
651 const base::FilePath& source_file_path,
652 const base::FilePath& device_file_path,
653 const CreateTemporaryFileCallback& create_temporary_file_callback,
654 const CopyFileProgressCallback& progress_callback,
655 const CopyFileLocalSuccessCallback& success_callback,
656 const ErrorCallback& error_callback) {
657 DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
658 DCHECK(!source_file_path.empty());
659 DCHECK(!device_file_path.empty());
661 // Create a temporary file for creating a copy of source file on local.
662 content::BrowserThread::PostTaskAndReplyWithResult(
663 content::BrowserThread::FILE, FROM_HERE, create_temporary_file_callback,
664 base::Bind(
665 &MTPDeviceDelegateImplLinux::OnDidCreateTemporaryFileToCopyFileLocal,
666 weak_ptr_factory_.GetWeakPtr(), source_file_path, device_file_path,
667 progress_callback, success_callback, error_callback));
670 void MTPDeviceDelegateImplLinux::MoveFileLocal(
671 const base::FilePath& source_file_path,
672 const base::FilePath& device_file_path,
673 const CreateTemporaryFileCallback& create_temporary_file_callback,
674 const MoveFileLocalSuccessCallback& success_callback,
675 const ErrorCallback& error_callback) {
676 DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
677 DCHECK(!source_file_path.empty());
678 DCHECK(!device_file_path.empty());
680 // Get file info to move file on local.
681 const GetFileInfoSuccessCallback success_callback_wrapper = base::Bind(
682 &MTPDeviceDelegateImplLinux::MoveFileLocalInternal,
683 weak_ptr_factory_.GetWeakPtr(), source_file_path, device_file_path,
684 create_temporary_file_callback, success_callback, error_callback);
685 const base::Closure closure =
686 base::Bind(&MTPDeviceDelegateImplLinux::GetFileInfoInternal,
687 weak_ptr_factory_.GetWeakPtr(), source_file_path,
688 success_callback_wrapper, error_callback);
689 EnsureInitAndRunTask(PendingTaskInfo(
690 source_file_path, content::BrowserThread::IO, FROM_HERE, closure));
693 void MTPDeviceDelegateImplLinux::CopyFileFromLocal(
694 const base::FilePath& source_file_path,
695 const base::FilePath& device_file_path,
696 const CopyFileFromLocalSuccessCallback& success_callback,
697 const ErrorCallback& error_callback) {
698 DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
699 DCHECK(!source_file_path.empty());
700 DCHECK(!device_file_path.empty());
702 // Get file info of destination file path.
703 const GetFileInfoSuccessCallback success_callback_wrapper = base::Bind(
704 &MTPDeviceDelegateImplLinux::OnDidGetDestFileInfoToCopyFileFromLocal,
705 weak_ptr_factory_.GetWeakPtr(), error_callback);
706 const ErrorCallback error_callback_wrapper = base::Bind(
707 &MTPDeviceDelegateImplLinux::OnGetDestFileInfoErrorToCopyFileFromLocal,
708 weak_ptr_factory_.GetWeakPtr(), source_file_path, device_file_path,
709 success_callback, error_callback);
710 const base::Closure closure =
711 base::Bind(&MTPDeviceDelegateImplLinux::GetFileInfoInternal,
712 weak_ptr_factory_.GetWeakPtr(), device_file_path,
713 success_callback_wrapper, error_callback_wrapper);
714 EnsureInitAndRunTask(PendingTaskInfo(
715 device_file_path, content::BrowserThread::IO, FROM_HERE, closure));
718 void MTPDeviceDelegateImplLinux::DeleteFile(
719 const base::FilePath& file_path,
720 const DeleteFileSuccessCallback& success_callback,
721 const ErrorCallback& error_callback) {
722 DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
723 DCHECK(!file_path.empty());
725 const GetFileInfoSuccessCallback& success_callback_wrapper =
726 base::Bind(&MTPDeviceDelegateImplLinux::DeleteFileInternal,
727 weak_ptr_factory_.GetWeakPtr(), file_path, success_callback,
728 error_callback);
730 const base::Closure closure =
731 base::Bind(&MTPDeviceDelegateImplLinux::GetFileInfoInternal,
732 weak_ptr_factory_.GetWeakPtr(), file_path,
733 success_callback_wrapper, error_callback);
734 EnsureInitAndRunTask(PendingTaskInfo(file_path, content::BrowserThread::IO,
735 FROM_HERE, closure));
738 void MTPDeviceDelegateImplLinux::DeleteDirectory(
739 const base::FilePath& file_path,
740 const DeleteDirectorySuccessCallback& success_callback,
741 const ErrorCallback& error_callback) {
742 DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
743 DCHECK(!file_path.empty());
745 const GetFileInfoSuccessCallback& success_callback_wrapper =
746 base::Bind(&MTPDeviceDelegateImplLinux::DeleteDirectoryInternal,
747 weak_ptr_factory_.GetWeakPtr(), file_path, success_callback,
748 error_callback);
750 const base::Closure closure =
751 base::Bind(&MTPDeviceDelegateImplLinux::GetFileInfoInternal,
752 weak_ptr_factory_.GetWeakPtr(), file_path,
753 success_callback_wrapper, error_callback);
754 EnsureInitAndRunTask(PendingTaskInfo(file_path, content::BrowserThread::IO,
755 FROM_HERE, closure));
758 void MTPDeviceDelegateImplLinux::AddWatcher(
759 const GURL& origin,
760 const base::FilePath& file_path,
761 const bool recursive,
762 const storage::WatcherManager::StatusCallback& callback,
763 const storage::WatcherManager::NotificationCallback&
764 notification_callback) {
765 if (recursive) {
766 callback.Run(base::File::FILE_ERROR_INVALID_OPERATION);
767 return;
770 const auto it = subscribers_.find(file_path);
771 if (it != subscribers_.end()) {
772 // Adds to existing origin callback map.
773 if (ContainsKey(it->second, origin)) {
774 callback.Run(base::File::FILE_ERROR_EXISTS);
775 return;
778 it->second.insert(std::make_pair(origin, notification_callback));
779 } else {
780 // Creates new origin callback map.
781 OriginNotificationCallbackMap callback_map;
782 callback_map.insert(std::make_pair(origin, notification_callback));
783 subscribers_.insert(std::make_pair(file_path, callback_map));
786 callback.Run(base::File::FILE_OK);
789 void MTPDeviceDelegateImplLinux::RemoveWatcher(
790 const GURL& origin,
791 const base::FilePath& file_path,
792 const bool recursive,
793 const storage::WatcherManager::StatusCallback& callback) {
794 if (recursive) {
795 callback.Run(base::File::FILE_ERROR_INVALID_OPERATION);
796 return;
799 const auto it = subscribers_.find(file_path);
800 if (it == subscribers_.end()) {
801 callback.Run(base::File::FILE_ERROR_NOT_FOUND);
802 return;
805 if (it->second.erase(origin) == 0) {
806 callback.Run(base::File::FILE_ERROR_NOT_FOUND);
807 return;
810 if (it->second.empty())
811 subscribers_.erase(it);
813 callback.Run(base::File::FILE_OK);
816 void MTPDeviceDelegateImplLinux::NotifyFileChange(
817 const base::FilePath& file_path,
818 const storage::WatcherManager::ChangeType change_type) {
819 const auto it = subscribers_.find(file_path);
820 if (it != subscribers_.end()) {
821 for (const auto& origin_callback : it->second) {
822 origin_callback.second.Run(change_type);
827 void MTPDeviceDelegateImplLinux::CancelPendingTasksAndDeleteDelegate() {
828 DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
829 // To cancel all the pending tasks, destroy the MTPDeviceTaskHelper object.
830 content::BrowserThread::PostTask(
831 content::BrowserThread::UI,
832 FROM_HERE,
833 base::Bind(&CloseStorageAndDestroyTaskHelperOnUIThread,
834 storage_name_,
835 read_only_));
836 delete this;
839 void MTPDeviceDelegateImplLinux::GetFileInfoInternal(
840 const base::FilePath& file_path,
841 const GetFileInfoSuccessCallback& success_callback,
842 const ErrorCallback& error_callback) {
843 DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
845 uint32 file_id;
846 if (CachedPathToId(file_path, &file_id)) {
847 GetFileInfoSuccessCallback success_callback_wrapper =
848 base::Bind(&MTPDeviceDelegateImplLinux::OnDidGetFileInfo,
849 weak_ptr_factory_.GetWeakPtr(),
850 success_callback);
851 ErrorCallback error_callback_wrapper =
852 base::Bind(&MTPDeviceDelegateImplLinux::HandleDeviceFileError,
853 weak_ptr_factory_.GetWeakPtr(),
854 error_callback,
855 file_id);
858 base::Closure closure = base::Bind(&GetFileInfoOnUIThread,
859 storage_name_,
860 read_only_,
861 file_id,
862 success_callback_wrapper,
863 error_callback_wrapper);
864 EnsureInitAndRunTask(PendingTaskInfo(base::FilePath(),
865 content::BrowserThread::UI,
866 FROM_HERE,
867 closure));
868 } else {
869 error_callback.Run(base::File::FILE_ERROR_NOT_FOUND);
871 PendingRequestDone();
874 void MTPDeviceDelegateImplLinux::CreateDirectoryInternal(
875 const std::vector<base::FilePath>& components,
876 const bool exclusive,
877 const CreateDirectorySuccessCallback& success_callback,
878 const ErrorCallback& error_callback) {
879 DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
881 const base::FilePath current_component = components.back();
882 std::vector<base::FilePath> other_components = components;
883 other_components.pop_back();
885 if (other_components.empty()) {
886 // Either we reached the last component in the recursive case, or this is
887 // the non-recursive case.
888 uint32 parent_id;
889 if (CachedPathToId(current_component.DirName(), &parent_id)) {
890 const base::Closure closure =
891 base::Bind(&MTPDeviceDelegateImplLinux::CreateSingleDirectory,
892 weak_ptr_factory_.GetWeakPtr(), current_component,
893 exclusive, success_callback, error_callback);
894 EnsureInitAndRunTask(PendingTaskInfo(
895 base::FilePath(), content::BrowserThread::IO, FROM_HERE, closure));
896 } else {
897 error_callback.Run(base::File::FILE_ERROR_NOT_FOUND);
899 } else {
900 // Ensures that parent directories are created for recursive case.
901 uint32 directory_id;
902 if (CachedPathToId(current_component, &directory_id)) {
903 // Parent directory |current_component| already exists, continue creating
904 // directories.
905 const base::Closure closure =
906 base::Bind(&MTPDeviceDelegateImplLinux::CreateDirectoryInternal,
907 weak_ptr_factory_.GetWeakPtr(), other_components,
908 exclusive, success_callback, error_callback);
909 EnsureInitAndRunTask(PendingTaskInfo(
910 base::FilePath(), content::BrowserThread::IO, FROM_HERE, closure));
911 } else {
912 // If parent directory |current_component| does not exist, create it.
913 const CreateDirectorySuccessCallback success_callback_wrapper =
914 base::Bind(&MTPDeviceDelegateImplLinux::
915 OnDidCreateParentDirectoryToCreateDirectory,
916 weak_ptr_factory_.GetWeakPtr(), current_component,
917 other_components, exclusive, success_callback,
918 error_callback);
919 // Wraps error callback to return all errors of creating parent
920 // directories as FILE_ERROR_FAILED.
921 const ErrorCallback error_callback_wrapper =
922 base::Bind(&MTPDeviceDelegateImplLinux::
923 OnCreateParentDirectoryErrorToCreateDirectory,
924 weak_ptr_factory_.GetWeakPtr(), error_callback);
925 const base::Closure closure =
926 base::Bind(&MTPDeviceDelegateImplLinux::CreateSingleDirectory,
927 weak_ptr_factory_.GetWeakPtr(), current_component,
928 false /* not exclusive */, success_callback_wrapper,
929 error_callback_wrapper);
930 EnsureInitAndRunTask(PendingTaskInfo(
931 base::FilePath(), content::BrowserThread::IO, FROM_HERE, closure));
935 PendingRequestDone();
938 void MTPDeviceDelegateImplLinux::ReadDirectoryInternal(
939 const base::FilePath& root,
940 const ReadDirectorySuccessCallback& success_callback,
941 const ErrorCallback& error_callback) {
942 DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
944 uint32 dir_id;
945 if (CachedPathToId(root, &dir_id)) {
946 GetFileInfoSuccessCallback success_callback_wrapper =
947 base::Bind(&MTPDeviceDelegateImplLinux::OnDidGetFileInfoToReadDirectory,
948 weak_ptr_factory_.GetWeakPtr(),
949 dir_id,
950 success_callback,
951 error_callback);
952 ErrorCallback error_callback_wrapper =
953 base::Bind(&MTPDeviceDelegateImplLinux::HandleDeviceFileError,
954 weak_ptr_factory_.GetWeakPtr(),
955 error_callback,
956 dir_id);
957 base::Closure closure = base::Bind(&GetFileInfoOnUIThread,
958 storage_name_,
959 read_only_,
960 dir_id,
961 success_callback_wrapper,
962 error_callback_wrapper);
963 EnsureInitAndRunTask(PendingTaskInfo(base::FilePath(),
964 content::BrowserThread::UI,
965 FROM_HERE,
966 closure));
967 } else {
968 error_callback.Run(base::File::FILE_ERROR_NOT_FOUND);
970 PendingRequestDone();
973 void MTPDeviceDelegateImplLinux::CreateSnapshotFileInternal(
974 const base::FilePath& device_file_path,
975 const base::FilePath& local_path,
976 const CreateSnapshotFileSuccessCallback& success_callback,
977 const ErrorCallback& error_callback) {
978 DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
980 uint32 file_id;
981 if (CachedPathToId(device_file_path, &file_id)) {
982 scoped_ptr<SnapshotRequestInfo> request_info(
983 new SnapshotRequestInfo(file_id,
984 local_path,
985 success_callback,
986 error_callback));
987 GetFileInfoSuccessCallback success_callback_wrapper =
988 base::Bind(
989 &MTPDeviceDelegateImplLinux::OnDidGetFileInfoToCreateSnapshotFile,
990 weak_ptr_factory_.GetWeakPtr(),
991 base::Passed(&request_info));
992 ErrorCallback error_callback_wrapper =
993 base::Bind(&MTPDeviceDelegateImplLinux::HandleDeviceFileError,
994 weak_ptr_factory_.GetWeakPtr(),
995 error_callback,
996 file_id);
997 base::Closure closure = base::Bind(&GetFileInfoOnUIThread,
998 storage_name_,
999 read_only_,
1000 file_id,
1001 success_callback_wrapper,
1002 error_callback_wrapper);
1003 EnsureInitAndRunTask(PendingTaskInfo(base::FilePath(),
1004 content::BrowserThread::UI,
1005 FROM_HERE,
1006 closure));
1007 } else {
1008 error_callback.Run(base::File::FILE_ERROR_NOT_FOUND);
1010 PendingRequestDone();
1013 void MTPDeviceDelegateImplLinux::ReadBytesInternal(
1014 const base::FilePath& device_file_path,
1015 net::IOBuffer* buf, int64 offset, int buf_len,
1016 const ReadBytesSuccessCallback& success_callback,
1017 const ErrorCallback& error_callback) {
1018 DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
1020 uint32 file_id;
1021 if (CachedPathToId(device_file_path, &file_id)) {
1022 ReadBytesRequest request(
1023 file_id, buf, offset, buf_len,
1024 base::Bind(&MTPDeviceDelegateImplLinux::OnDidReadBytes,
1025 weak_ptr_factory_.GetWeakPtr(),
1026 success_callback),
1027 base::Bind(&MTPDeviceDelegateImplLinux::HandleDeviceFileError,
1028 weak_ptr_factory_.GetWeakPtr(),
1029 error_callback,
1030 file_id));
1032 base::Closure closure =
1033 base::Bind(&ReadBytesOnUIThread, storage_name_, read_only_, request);
1034 EnsureInitAndRunTask(PendingTaskInfo(base::FilePath(),
1035 content::BrowserThread::UI,
1036 FROM_HERE,
1037 closure));
1038 } else {
1039 error_callback.Run(base::File::FILE_ERROR_NOT_FOUND);
1041 PendingRequestDone();
1044 void MTPDeviceDelegateImplLinux::MoveFileLocalInternal(
1045 const base::FilePath& source_file_path,
1046 const base::FilePath& device_file_path,
1047 const CreateTemporaryFileCallback& create_temporary_file_callback,
1048 const MoveFileLocalSuccessCallback& success_callback,
1049 const ErrorCallback& error_callback,
1050 const base::File::Info& source_file_info) {
1051 DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
1053 if (source_file_info.is_directory) {
1054 error_callback.Run(base::File::FILE_ERROR_NOT_A_FILE);
1055 return;
1058 if (source_file_path.DirName() == device_file_path.DirName()) {
1059 // If a file is moved in a same directory, rename the file.
1060 uint32 file_id;
1061 if (CachedPathToId(source_file_path, &file_id)) {
1062 const MTPDeviceTaskHelper::RenameObjectSuccessCallback
1063 success_callback_wrapper = base::Bind(
1064 &MTPDeviceDelegateImplLinux::OnDidMoveFileLocalWithRename,
1065 weak_ptr_factory_.GetWeakPtr(), success_callback,
1066 source_file_path, file_id);
1067 const MTPDeviceTaskHelper::ErrorCallback error_callback_wrapper =
1068 base::Bind(&MTPDeviceDelegateImplLinux::HandleDeviceFileError,
1069 weak_ptr_factory_.GetWeakPtr(), error_callback, file_id);
1070 const base::Closure closure =
1071 base::Bind(&RenameObjectOnUIThread, storage_name_, read_only_,
1072 file_id, device_file_path.BaseName().value(),
1073 success_callback_wrapper, error_callback_wrapper);
1074 EnsureInitAndRunTask(PendingTaskInfo(
1075 base::FilePath(), content::BrowserThread::UI, FROM_HERE, closure));
1076 } else {
1077 error_callback.Run(base::File::FILE_ERROR_NOT_FOUND);
1079 } else {
1080 // If a file is moved to a different directory, create a copy to the
1081 // destination path, and remove source file.
1082 const CopyFileLocalSuccessCallback& success_callback_wrapper =
1083 base::Bind(&MTPDeviceDelegateImplLinux::DeleteFileInternal,
1084 weak_ptr_factory_.GetWeakPtr(), source_file_path,
1085 success_callback, error_callback, source_file_info);
1086 // TODO(yawano): Avoid to call external method from internal code.
1087 CopyFileLocal(source_file_path, device_file_path,
1088 create_temporary_file_callback,
1089 base::Bind(&FakeCopyFileProgressCallback),
1090 success_callback_wrapper, error_callback);
1094 void MTPDeviceDelegateImplLinux::OnDidOpenFDToCopyFileFromLocal(
1095 const base::FilePath& device_file_path,
1096 const CopyFileFromLocalSuccessCallback& success_callback,
1097 const ErrorCallback& error_callback,
1098 const std::pair<int, base::File::Error>& open_fd_result) {
1099 DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
1101 if (open_fd_result.second != base::File::FILE_OK) {
1102 error_callback.Run(open_fd_result.second);
1103 return;
1106 const int source_file_descriptor = open_fd_result.first;
1107 uint32 parent_id;
1108 if (CachedPathToId(device_file_path.DirName(), &parent_id)) {
1109 CopyFileFromLocalSuccessCallback success_callback_wrapper =
1110 base::Bind(&MTPDeviceDelegateImplLinux::OnDidCopyFileFromLocal,
1111 weak_ptr_factory_.GetWeakPtr(), success_callback,
1112 device_file_path, source_file_descriptor);
1114 ErrorCallback error_callback_wrapper = base::Bind(
1115 &MTPDeviceDelegateImplLinux::HandleCopyFileFromLocalError,
1116 weak_ptr_factory_.GetWeakPtr(), error_callback, source_file_descriptor);
1118 base::Closure closure = base::Bind(&CopyFileFromLocalOnUIThread,
1119 storage_name_,
1120 read_only_,
1121 source_file_descriptor,
1122 parent_id,
1123 device_file_path.BaseName().value(),
1124 success_callback_wrapper,
1125 error_callback_wrapper);
1127 EnsureInitAndRunTask(PendingTaskInfo(
1128 base::FilePath(), content::BrowserThread::UI, FROM_HERE, closure));
1129 } else {
1130 HandleCopyFileFromLocalError(error_callback, source_file_descriptor,
1131 base::File::FILE_ERROR_NOT_FOUND);
1135 void MTPDeviceDelegateImplLinux::DeleteFileInternal(
1136 const base::FilePath& file_path,
1137 const DeleteFileSuccessCallback& success_callback,
1138 const ErrorCallback& error_callback,
1139 const base::File::Info& file_info) {
1140 DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
1142 if (file_info.is_directory) {
1143 error_callback.Run(base::File::FILE_ERROR_NOT_A_FILE);
1144 } else {
1145 uint32 file_id;
1146 if (CachedPathToId(file_path, &file_id))
1147 RunDeleteObjectOnUIThread(file_path, file_id, success_callback,
1148 error_callback);
1149 else
1150 error_callback.Run(base::File::FILE_ERROR_NOT_FOUND);
1154 void MTPDeviceDelegateImplLinux::DeleteDirectoryInternal(
1155 const base::FilePath& file_path,
1156 const DeleteDirectorySuccessCallback& success_callback,
1157 const ErrorCallback& error_callback,
1158 const base::File::Info& file_info) {
1159 DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
1161 if (!file_info.is_directory) {
1162 error_callback.Run(base::File::FILE_ERROR_NOT_A_DIRECTORY);
1163 return;
1166 uint32 directory_id;
1167 if (!CachedPathToId(file_path, &directory_id)) {
1168 error_callback.Run(base::File::FILE_ERROR_NOT_FOUND);
1169 return;
1172 // Checks the cache first. If it has children in cache, the directory cannot
1173 // be empty.
1174 FileIdToMTPFileNodeMap::const_iterator it =
1175 file_id_to_node_map_.find(directory_id);
1176 if (it != file_id_to_node_map_.end() && it->second->HasChildren()) {
1177 error_callback.Run(base::File::FILE_ERROR_NOT_EMPTY);
1178 return;
1181 // Since the directory can contain a file even if the cache returns it as
1182 // empty, read the directory and confirm the directory is actually empty.
1183 const MTPDeviceTaskHelper::ReadDirectorySuccessCallback
1184 success_callback_wrapper = base::Bind(
1185 &MTPDeviceDelegateImplLinux::OnDidReadDirectoryToDeleteDirectory,
1186 weak_ptr_factory_.GetWeakPtr(), file_path, directory_id,
1187 success_callback, error_callback);
1188 const MTPDeviceTaskHelper::ErrorCallback error_callback_wrapper =
1189 base::Bind(&MTPDeviceDelegateImplLinux::HandleDeviceFileError,
1190 weak_ptr_factory_.GetWeakPtr(), error_callback, directory_id);
1191 const base::Closure closure = base::Bind(
1192 &ReadDirectoryOnUIThread, storage_name_, read_only_, directory_id,
1193 1 /* max_size */, success_callback_wrapper, error_callback_wrapper);
1194 EnsureInitAndRunTask(PendingTaskInfo(
1195 base::FilePath(), content::BrowserThread::UI, FROM_HERE, closure));
1198 void MTPDeviceDelegateImplLinux::CreateSingleDirectory(
1199 const base::FilePath& directory_path,
1200 const bool exclusive,
1201 const CreateDirectorySuccessCallback& success_callback,
1202 const ErrorCallback& error_callback) {
1203 DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
1205 const GetFileInfoSuccessCallback success_callback_wrapper = base::Bind(
1206 &MTPDeviceDelegateImplLinux::OnPathAlreadyExistsForCreateSingleDirectory,
1207 weak_ptr_factory_.GetWeakPtr(), exclusive, success_callback,
1208 error_callback);
1209 const ErrorCallback error_callback_wrapper = base::Bind(
1210 &MTPDeviceDelegateImplLinux::OnPathDoesNotExistForCreateSingleDirectory,
1211 weak_ptr_factory_.GetWeakPtr(), directory_path, success_callback,
1212 error_callback);
1213 const base::Closure closure =
1214 base::Bind(&MTPDeviceDelegateImplLinux::GetFileInfoInternal,
1215 weak_ptr_factory_.GetWeakPtr(), directory_path,
1216 success_callback_wrapper, error_callback_wrapper);
1217 EnsureInitAndRunTask(PendingTaskInfo(
1218 base::FilePath(), content::BrowserThread::IO, FROM_HERE, closure));
1219 PendingRequestDone();
1222 void MTPDeviceDelegateImplLinux::OnDidReadDirectoryToCreateDirectory(
1223 const std::vector<base::FilePath>& components,
1224 const bool exclusive,
1225 const CreateDirectorySuccessCallback& success_callback,
1226 const ErrorCallback& error_callback,
1227 const storage::AsyncFileUtil::EntryList& /* file_list */,
1228 const bool has_more) {
1229 DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
1231 if (has_more)
1232 return; // Wait until all entries have been read.
1234 const base::Closure closure =
1235 base::Bind(&MTPDeviceDelegateImplLinux::CreateDirectoryInternal,
1236 weak_ptr_factory_.GetWeakPtr(), components, exclusive,
1237 success_callback, error_callback);
1238 EnsureInitAndRunTask(PendingTaskInfo(
1239 base::FilePath(), content::BrowserThread::IO, FROM_HERE, closure));
1242 void MTPDeviceDelegateImplLinux::OnDidReadDirectoryToDeleteDirectory(
1243 const base::FilePath& directory_path,
1244 const uint32 directory_id,
1245 const DeleteDirectorySuccessCallback& success_callback,
1246 const ErrorCallback& error_callback,
1247 const storage::AsyncFileUtil::EntryList& entries,
1248 const bool has_more) {
1249 DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
1250 DCHECK(!has_more);
1252 if (entries.size() > 0) {
1253 error_callback.Run(base::File::FILE_ERROR_NOT_EMPTY);
1254 } else {
1255 RunDeleteObjectOnUIThread(directory_path, directory_id, success_callback,
1256 error_callback);
1259 PendingRequestDone();
1262 void MTPDeviceDelegateImplLinux::RunDeleteObjectOnUIThread(
1263 const base::FilePath& object_path,
1264 const uint32 object_id,
1265 const DeleteObjectSuccessCallback& success_callback,
1266 const ErrorCallback& error_callback) {
1267 const MTPDeviceTaskHelper::DeleteObjectSuccessCallback
1268 success_callback_wrapper =
1269 base::Bind(&MTPDeviceDelegateImplLinux::OnDidDeleteObject,
1270 weak_ptr_factory_.GetWeakPtr(), object_path, object_id,
1271 success_callback);
1273 const MTPDeviceTaskHelper::ErrorCallback error_callback_wrapper =
1274 base::Bind(&MTPDeviceDelegateImplLinux::HandleDeleteFileOrDirectoryError,
1275 weak_ptr_factory_.GetWeakPtr(), error_callback);
1277 const base::Closure closure =
1278 base::Bind(&DeleteObjectOnUIThread, storage_name_, read_only_, object_id,
1279 success_callback_wrapper, error_callback_wrapper);
1280 EnsureInitAndRunTask(PendingTaskInfo(
1281 base::FilePath(), content::BrowserThread::UI, FROM_HERE, closure));
1284 void MTPDeviceDelegateImplLinux::EnsureInitAndRunTask(
1285 const PendingTaskInfo& task_info) {
1286 DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
1287 if ((init_state_ == INITIALIZED) && !task_in_progress_) {
1288 RunTask(task_info);
1289 return;
1292 // Only *Internal functions have empty paths. Since they are the continuation
1293 // of the current running task, they get to cut in line.
1294 if (task_info.path.empty())
1295 pending_tasks_.push_front(task_info);
1296 else
1297 pending_tasks_.push_back(task_info);
1299 if (init_state_ == UNINITIALIZED) {
1300 init_state_ = PENDING_INIT;
1301 task_in_progress_ = true;
1302 content::BrowserThread::PostTask(
1303 content::BrowserThread::UI, FROM_HERE,
1304 base::Bind(&OpenStorageOnUIThread, storage_name_, read_only_,
1305 base::Bind(&MTPDeviceDelegateImplLinux::OnInitCompleted,
1306 weak_ptr_factory_.GetWeakPtr())));
1310 void MTPDeviceDelegateImplLinux::RunTask(const PendingTaskInfo& task_info) {
1311 DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
1312 DCHECK_EQ(INITIALIZED, init_state_);
1313 DCHECK(!task_in_progress_);
1314 task_in_progress_ = true;
1316 bool need_to_check_cache = !task_info.path.empty();
1317 if (need_to_check_cache) {
1318 base::FilePath uncached_path =
1319 NextUncachedPathComponent(task_info.path, task_info.cached_path);
1320 if (!uncached_path.empty()) {
1321 // Save the current task and do a cache lookup first.
1322 pending_tasks_.push_front(task_info);
1323 FillFileCache(uncached_path);
1324 return;
1328 content::BrowserThread::PostTask(task_info.thread_id,
1329 task_info.location,
1330 task_info.task);
1333 void MTPDeviceDelegateImplLinux::WriteDataIntoSnapshotFile(
1334 const base::File::Info& file_info) {
1335 DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
1336 DCHECK(current_snapshot_request_info_.get());
1337 DCHECK_GT(file_info.size, 0);
1338 DCHECK(task_in_progress_);
1339 SnapshotRequestInfo request_info(
1340 current_snapshot_request_info_->file_id,
1341 current_snapshot_request_info_->snapshot_file_path,
1342 base::Bind(
1343 &MTPDeviceDelegateImplLinux::OnDidWriteDataIntoSnapshotFile,
1344 weak_ptr_factory_.GetWeakPtr()),
1345 base::Bind(
1346 &MTPDeviceDelegateImplLinux::OnWriteDataIntoSnapshotFileError,
1347 weak_ptr_factory_.GetWeakPtr()));
1349 base::Closure task_closure = base::Bind(&WriteDataIntoSnapshotFileOnUIThread,
1350 storage_name_,
1351 read_only_,
1352 request_info,
1353 file_info);
1354 content::BrowserThread::PostTask(content::BrowserThread::UI,
1355 FROM_HERE,
1356 task_closure);
1359 void MTPDeviceDelegateImplLinux::PendingRequestDone() {
1360 DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
1361 DCHECK(task_in_progress_);
1362 task_in_progress_ = false;
1363 ProcessNextPendingRequest();
1366 void MTPDeviceDelegateImplLinux::ProcessNextPendingRequest() {
1367 DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
1368 DCHECK(!task_in_progress_);
1369 if (pending_tasks_.empty())
1370 return;
1372 PendingTaskInfo task_info = pending_tasks_.front();
1373 pending_tasks_.pop_front();
1374 RunTask(task_info);
1377 void MTPDeviceDelegateImplLinux::OnInitCompleted(bool succeeded) {
1378 DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
1379 init_state_ = succeeded ? INITIALIZED : UNINITIALIZED;
1380 PendingRequestDone();
1383 void MTPDeviceDelegateImplLinux::OnDidGetFileInfo(
1384 const GetFileInfoSuccessCallback& success_callback,
1385 const base::File::Info& file_info) {
1386 DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
1387 success_callback.Run(file_info);
1388 PendingRequestDone();
1391 void MTPDeviceDelegateImplLinux::OnPathAlreadyExistsForCreateSingleDirectory(
1392 const bool exclusive,
1393 const CreateDirectorySuccessCallback& success_callback,
1394 const ErrorCallback& error_callback,
1395 const base::File::Info& file_info) {
1396 DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
1398 if (!file_info.is_directory || exclusive)
1399 error_callback.Run(base::File::FILE_ERROR_EXISTS);
1400 else
1401 success_callback.Run();
1404 void MTPDeviceDelegateImplLinux::OnPathDoesNotExistForCreateSingleDirectory(
1405 const base::FilePath& directory_path,
1406 const CreateDirectorySuccessCallback& success_callback,
1407 const ErrorCallback& error_callback,
1408 const base::File::Error error) {
1409 DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
1411 if (error != base::File::FILE_ERROR_NOT_FOUND) {
1412 error_callback.Run(base::File::FILE_ERROR_NOT_FOUND);
1413 return;
1416 uint32 parent_id;
1417 if (!CachedPathToId(directory_path.DirName(), &parent_id)) {
1418 error_callback.Run(base::File::FILE_ERROR_NOT_FOUND);
1419 return;
1422 const MTPDeviceTaskHelper::CreateDirectorySuccessCallback
1423 success_callback_wrapper = base::Bind(
1424 &MTPDeviceDelegateImplLinux::OnDidCreateSingleDirectory,
1425 weak_ptr_factory_.GetWeakPtr(), directory_path, success_callback);
1426 const MTPDeviceTaskHelper::ErrorCallback error_callback_wrapper =
1427 base::Bind(&MTPDeviceDelegateImplLinux::HandleDeviceFileError,
1428 weak_ptr_factory_.GetWeakPtr(), error_callback, parent_id);
1429 const base::Closure closure =
1430 base::Bind(&CreateDirectoryOnUIThread, storage_name_, read_only_,
1431 parent_id, directory_path.BaseName().value(),
1432 success_callback_wrapper, error_callback_wrapper);
1433 EnsureInitAndRunTask(PendingTaskInfo(
1434 base::FilePath(), content::BrowserThread::UI, FROM_HERE, closure));
1437 void MTPDeviceDelegateImplLinux::OnDidGetFileInfoToReadDirectory(
1438 uint32 dir_id,
1439 const ReadDirectorySuccessCallback& success_callback,
1440 const ErrorCallback& error_callback,
1441 const base::File::Info& file_info) {
1442 DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
1443 DCHECK(task_in_progress_);
1444 if (!file_info.is_directory) {
1445 return HandleDeviceFileError(error_callback,
1446 dir_id,
1447 base::File::FILE_ERROR_NOT_A_DIRECTORY);
1450 base::Closure task_closure = base::Bind(
1451 &ReadDirectoryOnUIThread, storage_name_, read_only_, dir_id,
1452 0 /* max_size */,
1453 base::Bind(&MTPDeviceDelegateImplLinux::OnDidReadDirectory,
1454 weak_ptr_factory_.GetWeakPtr(), dir_id, success_callback),
1455 base::Bind(&MTPDeviceDelegateImplLinux::HandleDeviceFileError,
1456 weak_ptr_factory_.GetWeakPtr(), error_callback, dir_id));
1457 content::BrowserThread::PostTask(content::BrowserThread::UI,
1458 FROM_HERE,
1459 task_closure);
1462 void MTPDeviceDelegateImplLinux::OnDidGetFileInfoToCreateSnapshotFile(
1463 scoped_ptr<SnapshotRequestInfo> snapshot_request_info,
1464 const base::File::Info& file_info) {
1465 DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
1466 DCHECK(!current_snapshot_request_info_.get());
1467 DCHECK(snapshot_request_info.get());
1468 DCHECK(task_in_progress_);
1469 base::File::Error error = base::File::FILE_OK;
1470 if (file_info.is_directory)
1471 error = base::File::FILE_ERROR_NOT_A_FILE;
1472 else if (file_info.size < 0 || file_info.size > kuint32max)
1473 error = base::File::FILE_ERROR_FAILED;
1475 if (error != base::File::FILE_OK)
1476 return HandleDeviceFileError(snapshot_request_info->error_callback,
1477 snapshot_request_info->file_id,
1478 error);
1480 base::File::Info snapshot_file_info(file_info);
1481 // Modify the last modified time to null. This prevents the time stamp
1482 // verfication in LocalFileStreamReader.
1483 snapshot_file_info.last_modified = base::Time();
1485 current_snapshot_request_info_.reset(snapshot_request_info.release());
1486 if (file_info.size == 0) {
1487 // Empty snapshot file.
1488 return OnDidWriteDataIntoSnapshotFile(
1489 snapshot_file_info, current_snapshot_request_info_->snapshot_file_path);
1491 WriteDataIntoSnapshotFile(snapshot_file_info);
1494 void MTPDeviceDelegateImplLinux::OnDidGetDestFileInfoToCopyFileFromLocal(
1495 const ErrorCallback& error_callback,
1496 const base::File::Info& file_info) {
1497 DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
1499 if (file_info.is_directory)
1500 error_callback.Run(base::File::FILE_ERROR_INVALID_OPERATION);
1501 else
1502 error_callback.Run(base::File::FILE_ERROR_FAILED);
1505 void MTPDeviceDelegateImplLinux::OnGetDestFileInfoErrorToCopyFileFromLocal(
1506 const base::FilePath& source_file_path,
1507 const base::FilePath& device_file_path,
1508 const CopyFileFromLocalSuccessCallback& success_callback,
1509 const ErrorCallback& error_callback,
1510 const base::File::Error error) {
1511 DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
1513 if (error != base::File::FILE_ERROR_NOT_FOUND) {
1514 error_callback.Run(error);
1515 return;
1518 content::BrowserThread::PostTaskAndReplyWithResult(
1519 content::BrowserThread::FILE, FROM_HERE,
1520 base::Bind(&OpenFileDescriptor, source_file_path.value().c_str(),
1521 O_RDONLY),
1522 base::Bind(&MTPDeviceDelegateImplLinux::OnDidOpenFDToCopyFileFromLocal,
1523 weak_ptr_factory_.GetWeakPtr(), device_file_path,
1524 success_callback, error_callback));
1527 void MTPDeviceDelegateImplLinux::OnDidCreateSingleDirectory(
1528 const base::FilePath& directory_path,
1529 const CreateDirectorySuccessCallback& success_callback) {
1530 DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
1532 success_callback.Run();
1533 NotifyFileChange(directory_path.DirName(),
1534 storage::WatcherManager::ChangeType::CHANGED);
1535 PendingRequestDone();
1538 void MTPDeviceDelegateImplLinux::OnDidCreateParentDirectoryToCreateDirectory(
1539 const base::FilePath& created_directory,
1540 const std::vector<base::FilePath>& components,
1541 const bool exclusive,
1542 const CreateDirectorySuccessCallback& success_callback,
1543 const ErrorCallback& error_callback) {
1544 DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
1546 // Calls ReadDirectoryInternal to fill the cache for created directory.
1547 // Calls ReadDirectoryInternal in this method to call it via
1548 // EnsureInitAndRunTask.
1549 const ReadDirectorySuccessCallback& success_callback_wrapper = base::Bind(
1550 &MTPDeviceDelegateImplLinux::OnDidReadDirectoryToCreateDirectory,
1551 weak_ptr_factory_.GetWeakPtr(), components, exclusive, success_callback,
1552 error_callback);
1553 const base::Closure closure =
1554 base::Bind(&MTPDeviceDelegateImplLinux::ReadDirectoryInternal,
1555 weak_ptr_factory_.GetWeakPtr(), created_directory.DirName(),
1556 success_callback_wrapper, error_callback);
1557 EnsureInitAndRunTask(PendingTaskInfo(
1558 base::FilePath(), content::BrowserThread::IO, FROM_HERE, closure));
1561 void MTPDeviceDelegateImplLinux::OnCreateParentDirectoryErrorToCreateDirectory(
1562 const ErrorCallback& callback,
1563 const base::File::Error error) {
1564 DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
1566 callback.Run(base::File::FILE_ERROR_FAILED);
1569 void MTPDeviceDelegateImplLinux::OnDidReadDirectory(
1570 uint32 dir_id,
1571 const ReadDirectorySuccessCallback& success_callback,
1572 const storage::AsyncFileUtil::EntryList& file_list,
1573 bool has_more) {
1574 DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
1576 FileIdToMTPFileNodeMap::iterator it = file_id_to_node_map_.find(dir_id);
1577 DCHECK(it != file_id_to_node_map_.end());
1578 MTPFileNode* dir_node = it->second;
1580 // Traverse the MTPFileNode tree to reconstuct the full path for |dir_id|.
1581 std::deque<std::string> dir_path_parts;
1582 MTPFileNode* parent_node = dir_node;
1583 while (parent_node->parent()) {
1584 dir_path_parts.push_front(parent_node->file_name());
1585 parent_node = parent_node->parent();
1587 base::FilePath dir_path = device_path_;
1588 for (size_t i = 0; i < dir_path_parts.size(); ++i)
1589 dir_path = dir_path.Append(dir_path_parts[i]);
1591 storage::AsyncFileUtil::EntryList normalized_file_list;
1592 for (size_t i = 0; i < file_list.size(); ++i) {
1593 normalized_file_list.push_back(file_list[i]);
1594 storage::DirectoryEntry& entry = normalized_file_list.back();
1596 // |entry.name| has the file id encoded in it. Decode here.
1597 size_t separator_idx = entry.name.find_last_of(',');
1598 DCHECK_NE(std::string::npos, separator_idx);
1599 std::string file_id_str = entry.name.substr(separator_idx);
1600 file_id_str = file_id_str.substr(1); // Get rid of the comma.
1601 uint32 file_id = 0;
1602 bool ret = base::StringToUint(file_id_str, &file_id);
1603 DCHECK(ret);
1604 entry.name = entry.name.substr(0, separator_idx);
1606 // Refresh the in memory tree.
1607 dir_node->EnsureChildExists(entry.name, file_id);
1608 child_nodes_seen_.insert(entry.name);
1610 // Add to |file_info_cache_|.
1611 file_info_cache_[dir_path.Append(entry.name)] = entry;
1614 success_callback.Run(normalized_file_list, has_more);
1615 if (has_more)
1616 return; // Wait to be called again.
1618 // Last call, finish book keeping and continue with the next request.
1619 dir_node->ClearNonexistentChildren(child_nodes_seen_);
1620 child_nodes_seen_.clear();
1621 file_info_cache_.clear();
1623 PendingRequestDone();
1626 void MTPDeviceDelegateImplLinux::OnDidWriteDataIntoSnapshotFile(
1627 const base::File::Info& file_info,
1628 const base::FilePath& snapshot_file_path) {
1629 DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
1630 DCHECK(current_snapshot_request_info_.get());
1631 current_snapshot_request_info_->success_callback.Run(
1632 file_info, snapshot_file_path);
1633 current_snapshot_request_info_.reset();
1634 PendingRequestDone();
1637 void MTPDeviceDelegateImplLinux::OnWriteDataIntoSnapshotFileError(
1638 base::File::Error error) {
1639 DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
1640 DCHECK(current_snapshot_request_info_.get());
1641 current_snapshot_request_info_->error_callback.Run(error);
1642 current_snapshot_request_info_.reset();
1643 PendingRequestDone();
1646 void MTPDeviceDelegateImplLinux::OnDidReadBytes(
1647 const ReadBytesSuccessCallback& success_callback,
1648 const base::File::Info& file_info, int bytes_read) {
1649 DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
1650 success_callback.Run(file_info, bytes_read);
1651 PendingRequestDone();
1654 void MTPDeviceDelegateImplLinux::OnDidFillFileCache(
1655 const base::FilePath& path,
1656 const storage::AsyncFileUtil::EntryList& /* file_list */,
1657 bool has_more) {
1658 DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
1659 DCHECK(path.IsParent(pending_tasks_.front().path));
1660 if (has_more)
1661 return; // Wait until all entries have been read.
1662 pending_tasks_.front().cached_path = path;
1665 void MTPDeviceDelegateImplLinux::OnFillFileCacheFailed(
1666 base::File::Error /* error */) {
1667 DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
1668 // When filling the cache fails for the task at the front of the queue, clear
1669 // the path of the task so it will not try to do any more caching. Instead,
1670 // the task will just run and fail the CachedPathToId() lookup.
1671 pending_tasks_.front().path.clear();
1674 void MTPDeviceDelegateImplLinux::OnDidCreateTemporaryFileToCopyFileLocal(
1675 const base::FilePath& source_file_path,
1676 const base::FilePath& device_file_path,
1677 const CopyFileProgressCallback& progress_callback,
1678 const CopyFileLocalSuccessCallback& success_callback,
1679 const ErrorCallback& error_callback,
1680 const base::FilePath& temporary_file_path) {
1681 DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
1683 if (temporary_file_path.empty()) {
1684 error_callback.Run(base::File::FILE_ERROR_FAILED);
1685 return;
1688 CreateSnapshotFile(
1689 source_file_path, temporary_file_path,
1690 base::Bind(
1691 &MTPDeviceDelegateImplLinux::OnDidCreateSnapshotFileOfCopyFileLocal,
1692 weak_ptr_factory_.GetWeakPtr(), device_file_path, progress_callback,
1693 success_callback, error_callback),
1694 base::Bind(&MTPDeviceDelegateImplLinux::HandleCopyFileLocalError,
1695 weak_ptr_factory_.GetWeakPtr(), error_callback,
1696 temporary_file_path));
1699 void MTPDeviceDelegateImplLinux::OnDidCreateSnapshotFileOfCopyFileLocal(
1700 const base::FilePath& device_file_path,
1701 const CopyFileProgressCallback& progress_callback,
1702 const CopyFileLocalSuccessCallback& success_callback,
1703 const ErrorCallback& error_callback,
1704 const base::File::Info& file_info,
1705 const base::FilePath& temporary_file_path) {
1706 DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
1708 // Consider that half of copy is completed by creating a temporary file.
1709 progress_callback.Run(file_info.size / 2);
1711 // TODO(yawano): Avoid to call external method from internal code.
1712 CopyFileFromLocal(
1713 temporary_file_path, device_file_path,
1714 base::Bind(
1715 &MTPDeviceDelegateImplLinux::OnDidCopyFileFromLocalOfCopyFileLocal,
1716 weak_ptr_factory_.GetWeakPtr(), success_callback,
1717 temporary_file_path),
1718 base::Bind(&MTPDeviceDelegateImplLinux::HandleCopyFileLocalError,
1719 weak_ptr_factory_.GetWeakPtr(), error_callback,
1720 temporary_file_path));
1723 void MTPDeviceDelegateImplLinux::OnDidCopyFileFromLocalOfCopyFileLocal(
1724 const CopyFileFromLocalSuccessCallback success_callback,
1725 const base::FilePath& temporary_file_path) {
1726 DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
1728 DeleteTemporaryFile(temporary_file_path);
1729 success_callback.Run();
1732 void MTPDeviceDelegateImplLinux::OnDidMoveFileLocalWithRename(
1733 const MoveFileLocalSuccessCallback& success_callback,
1734 const base::FilePath& source_file_path,
1735 const uint32 file_id) {
1736 DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
1738 EvictCachedPathToId(file_id);
1739 success_callback.Run();
1740 NotifyFileChange(source_file_path,
1741 storage::WatcherManager::ChangeType::DELETED);
1742 NotifyFileChange(source_file_path.DirName(),
1743 storage::WatcherManager::ChangeType::CHANGED);
1744 PendingRequestDone();
1747 void MTPDeviceDelegateImplLinux::OnDidCopyFileFromLocal(
1748 const CopyFileFromLocalSuccessCallback& success_callback,
1749 const base::FilePath& file_path,
1750 const int source_file_descriptor) {
1751 DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
1753 const base::Closure closure = base::Bind(&CloseFileDescriptor,
1754 source_file_descriptor);
1756 content::BrowserThread::PostTask(content::BrowserThread::FILE, FROM_HERE,
1757 closure);
1759 success_callback.Run();
1760 NotifyFileChange(file_path.DirName(),
1761 storage::WatcherManager::ChangeType::CHANGED);
1762 PendingRequestDone();
1765 void MTPDeviceDelegateImplLinux::HandleCopyFileLocalError(
1766 const ErrorCallback& error_callback,
1767 const base::FilePath& temporary_file_path,
1768 const base::File::Error error) {
1769 DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
1771 DeleteTemporaryFile(temporary_file_path);
1772 error_callback.Run(error);
1775 void MTPDeviceDelegateImplLinux::HandleCopyFileFromLocalError(
1776 const ErrorCallback& error_callback,
1777 const int source_file_descriptor,
1778 base::File::Error error) {
1779 DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
1781 const base::Closure closure = base::Bind(&CloseFileDescriptor,
1782 source_file_descriptor);
1784 content::BrowserThread::PostTask(content::BrowserThread::FILE, FROM_HERE,
1785 closure);
1787 error_callback.Run(error);
1788 PendingRequestDone();
1791 void MTPDeviceDelegateImplLinux::OnDidDeleteObject(
1792 const base::FilePath& object_path,
1793 const uint32 object_id,
1794 const DeleteObjectSuccessCallback success_callback) {
1795 DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
1797 EvictCachedPathToId(object_id);
1798 success_callback.Run();
1799 NotifyFileChange(object_path, storage::WatcherManager::ChangeType::DELETED);
1800 NotifyFileChange(object_path.DirName(),
1801 storage::WatcherManager::ChangeType::CHANGED);
1802 PendingRequestDone();
1805 void MTPDeviceDelegateImplLinux::HandleDeleteFileOrDirectoryError(
1806 const ErrorCallback& error_callback,
1807 base::File::Error error) {
1808 DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
1810 error_callback.Run(error);
1811 PendingRequestDone();
1814 void MTPDeviceDelegateImplLinux::HandleDeviceFileError(
1815 const ErrorCallback& error_callback,
1816 uint32 file_id,
1817 base::File::Error error) {
1818 DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
1820 EvictCachedPathToId(file_id);
1821 error_callback.Run(error);
1822 PendingRequestDone();
1825 base::FilePath MTPDeviceDelegateImplLinux::NextUncachedPathComponent(
1826 const base::FilePath& path,
1827 const base::FilePath& cached_path) const {
1828 DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
1829 DCHECK(cached_path.empty() || cached_path.IsParent(path));
1831 base::FilePath uncached_path;
1832 std::string device_relpath = GetDeviceRelativePath(device_path_, path);
1833 if (!device_relpath.empty() && device_relpath != kRootPath) {
1834 uncached_path = device_path_;
1835 std::vector<std::string> device_relpath_components = base::SplitString(
1836 device_relpath, "/", base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL);
1837 DCHECK(!device_relpath_components.empty());
1838 bool all_components_cached = true;
1839 const MTPFileNode* current_node = root_node_.get();
1840 for (size_t i = 0; i < device_relpath_components.size(); ++i) {
1841 current_node = current_node->GetChild(device_relpath_components[i]);
1842 if (!current_node) {
1843 // With a cache miss, check if it is a genuine failure. If so, pretend
1844 // the entire |path| is cached, so there is no further attempt to do
1845 // more caching. The actual operation will then fail.
1846 all_components_cached =
1847 !cached_path.empty() && (uncached_path == cached_path);
1848 break;
1850 uncached_path = uncached_path.Append(device_relpath_components[i]);
1852 if (all_components_cached)
1853 uncached_path.clear();
1855 return uncached_path;
1858 void MTPDeviceDelegateImplLinux::FillFileCache(
1859 const base::FilePath& uncached_path) {
1860 DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
1861 DCHECK(task_in_progress_);
1863 ReadDirectorySuccessCallback success_callback =
1864 base::Bind(&MTPDeviceDelegateImplLinux::OnDidFillFileCache,
1865 weak_ptr_factory_.GetWeakPtr(),
1866 uncached_path);
1867 ErrorCallback error_callback =
1868 base::Bind(&MTPDeviceDelegateImplLinux::OnFillFileCacheFailed,
1869 weak_ptr_factory_.GetWeakPtr());
1870 ReadDirectoryInternal(uncached_path, success_callback, error_callback);
1874 bool MTPDeviceDelegateImplLinux::CachedPathToId(const base::FilePath& path,
1875 uint32* id) const {
1876 DCHECK(id);
1878 std::string device_relpath = GetDeviceRelativePath(device_path_, path);
1879 if (device_relpath.empty())
1880 return false;
1881 std::vector<std::string> device_relpath_components;
1882 if (device_relpath != kRootPath) {
1883 device_relpath_components = base::SplitString(
1884 device_relpath, "/", base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL);
1886 const MTPFileNode* current_node = root_node_.get();
1887 for (size_t i = 0; i < device_relpath_components.size(); ++i) {
1888 current_node = current_node->GetChild(device_relpath_components[i]);
1889 if (!current_node)
1890 return false;
1892 *id = current_node->file_id();
1893 return true;
1896 void MTPDeviceDelegateImplLinux::EvictCachedPathToId(const uint32 id) {
1897 FileIdToMTPFileNodeMap::iterator it = file_id_to_node_map_.find(id);
1898 if (it != file_id_to_node_map_.end()) {
1899 DCHECK(!it->second->HasChildren());
1900 MTPFileNode* parent = it->second->parent();
1901 if (parent) {
1902 const bool ret = parent->DeleteChild(id);
1903 DCHECK(ret);
1908 void CreateMTPDeviceAsyncDelegate(
1909 const std::string& device_location,
1910 const bool read_only,
1911 const CreateMTPDeviceAsyncDelegateCallback& callback) {
1912 DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
1913 callback.Run(new MTPDeviceDelegateImplLinux(device_location, read_only));