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