1 // Copyright (c) 2013 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/fileapi/device_media_async_file_util.h"
6 
7 #include <stddef.h>
8 
9 #include <utility>
10 
11 #include "base/bind.h"
12 #include "base/callback.h"
13 #include "base/callback_helpers.h"
14 #include "base/files/file_util.h"
15 #include "base/macros.h"
16 #include "base/memory/ptr_util.h"
17 #include "base/single_thread_task_runner.h"
18 #include "base/task_runner_util.h"
19 #include "chrome/browser/media_galleries/fileapi/media_path_filter.h"
20 #include "chrome/browser/media_galleries/fileapi/mtp_device_async_delegate.h"
21 #include "chrome/browser/media_galleries/fileapi/mtp_device_map_service.h"
22 #include "chrome/browser/media_galleries/fileapi/mtp_file_stream_reader.h"
23 #include "chrome/browser/media_galleries/fileapi/native_media_file_util.h"
24 #include "chrome/browser/media_galleries/fileapi/readahead_file_stream_reader.h"
25 #include "components/services/filesystem/public/mojom/types.mojom.h"
26 #include "content/public/browser/browser_thread.h"
27 #include "storage/browser/blob/shareable_file_reference.h"
28 #include "storage/browser/file_system/file_stream_reader.h"
29 #include "storage/browser/file_system/file_system_context.h"
30 #include "storage/browser/file_system/file_system_operation_context.h"
31 #include "storage/browser/file_system/file_system_url.h"
32 #include "storage/browser/file_system/native_file_util.h"
33 
34 using storage::AsyncFileUtil;
35 using storage::FileSystemOperationContext;
36 using storage::FileSystemURL;
37 using storage::ShareableFileReference;
38 
39 namespace {
40 
41 const char kDeviceMediaAsyncFileUtilTempDir[] = "DeviceMediaFileSystem";
42 
43 // Called when GetFileInfo method call failed to get the details of file
44 // specified by the requested url. |callback| is invoked to notify the
45 // caller about the file |error|.
OnGetFileInfoError(AsyncFileUtil::GetFileInfoCallback callback,base::File::Error error)46 void OnGetFileInfoError(AsyncFileUtil::GetFileInfoCallback callback,
47                         base::File::Error error) {
48   DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
49   std::move(callback).Run(error, base::File::Info());
50 }
51 
52 // Called after OnDidGetFileInfo finishes media check.
53 // |callback| is invoked to complete the GetFileInfo request.
OnDidCheckMediaForGetFileInfo(AsyncFileUtil::GetFileInfoCallback callback,const base::File::Info & file_info,bool is_valid_file)54 void OnDidCheckMediaForGetFileInfo(AsyncFileUtil::GetFileInfoCallback callback,
55                                    const base::File::Info& file_info,
56                                    bool is_valid_file) {
57   DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
58   if (!is_valid_file) {
59     OnGetFileInfoError(std::move(callback), base::File::FILE_ERROR_NOT_FOUND);
60     return;
61   }
62   std::move(callback).Run(base::File::FILE_OK, file_info);
63 }
64 
65 // Called after OnDidReadDirectory finishes media check.
66 // |callback| is invoked to complete the ReadDirectory request.
OnDidCheckMediaForReadDirectory(AsyncFileUtil::ReadDirectoryCallback callback,bool has_more,AsyncFileUtil::EntryList file_list)67 void OnDidCheckMediaForReadDirectory(
68     AsyncFileUtil::ReadDirectoryCallback callback,
69     bool has_more,
70     AsyncFileUtil::EntryList file_list) {
71   DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
72   callback.Run(base::File::FILE_OK, std::move(file_list), has_more);
73 }
74 
75 // Called when CreateDirectory method call failed.
OnCreateDirectoryError(AsyncFileUtil::StatusCallback callback,base::File::Error error)76 void OnCreateDirectoryError(AsyncFileUtil::StatusCallback callback,
77                             base::File::Error error) {
78   DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
79   std::move(callback).Run(error);
80 }
81 
82 // Called when ReadDirectory method call failed to enumerate the directory
83 // objects. |callback| is invoked to notify the caller about the |error|
84 // that occured while reading the directory objects.
OnReadDirectoryError(AsyncFileUtil::ReadDirectoryCallback callback,base::File::Error error)85 void OnReadDirectoryError(AsyncFileUtil::ReadDirectoryCallback callback,
86                           base::File::Error error) {
87   DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
88   callback.Run(error, AsyncFileUtil::EntryList(), false /*no more*/);
89 }
90 
91 // Called when CopyFileLocal method call failed.
OnCopyFileLocalError(AsyncFileUtil::StatusCallback callback,base::File::Error error)92 void OnCopyFileLocalError(AsyncFileUtil::StatusCallback callback,
93                           base::File::Error error) {
94   DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
95   std::move(callback).Run(error);
96 }
97 
98 // Called when MoveFileLocal method call failed.
OnMoveFileLocalError(AsyncFileUtil::StatusCallback callback,base::File::Error error)99 void OnMoveFileLocalError(AsyncFileUtil::StatusCallback callback,
100                           base::File::Error error) {
101   DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
102   std::move(callback).Run(error);
103 }
104 
105 // Called when CopyInForeignFile method call failed.
OnCopyInForeignFileError(AsyncFileUtil::StatusCallback callback,base::File::Error error)106 void OnCopyInForeignFileError(AsyncFileUtil::StatusCallback callback,
107                               base::File::Error error) {
108   DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
109   std::move(callback).Run(error);
110 }
111 
112 // Called when DeleteFile method call failed.
OnDeleteFileError(AsyncFileUtil::StatusCallback callback,base::File::Error error)113 void OnDeleteFileError(AsyncFileUtil::StatusCallback callback,
114                        base::File::Error error) {
115   DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
116   std::move(callback).Run(error);
117 }
118 
119 // Called when DeleteDirectory method call failed.
OnDeleteDirectoryError(AsyncFileUtil::StatusCallback callback,base::File::Error error)120 void OnDeleteDirectoryError(AsyncFileUtil::StatusCallback callback,
121                             base::File::Error error) {
122   DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
123   std::move(callback).Run(error);
124 }
125 
126 // Called on a blocking pool thread to create a snapshot file to hold the
127 // contents of |device_file_path|. The snapshot file is created in the
128 // "profile_path/kDeviceMediaAsyncFileUtilTempDir" directory. Return the
129 // snapshot file path or an empty path on failure.
CreateSnapshotFileOnBlockingPool(const base::FilePath & profile_path)130 base::FilePath CreateSnapshotFileOnBlockingPool(
131     const base::FilePath& profile_path) {
132   base::FilePath snapshot_file_path;
133   base::FilePath media_file_system_dir_path =
134       profile_path.AppendASCII(kDeviceMediaAsyncFileUtilTempDir);
135   if (!base::CreateDirectory(media_file_system_dir_path) ||
136       !base::CreateTemporaryFileInDir(media_file_system_dir_path,
137                                       &snapshot_file_path)) {
138     LOG(WARNING) << "Could not create media snapshot file "
139                  << media_file_system_dir_path.value();
140     snapshot_file_path = base::FilePath();
141   }
142   return snapshot_file_path;
143 }
144 
145 // Called after OnDidCreateSnapshotFile finishes media check.
146 // |callback| is invoked to complete the CreateSnapshotFile request.
OnDidCheckMediaForCreateSnapshotFile(AsyncFileUtil::CreateSnapshotFileCallback callback,const base::File::Info & file_info,scoped_refptr<storage::ShareableFileReference> platform_file,base::File::Error error)147 void OnDidCheckMediaForCreateSnapshotFile(
148     AsyncFileUtil::CreateSnapshotFileCallback callback,
149     const base::File::Info& file_info,
150     scoped_refptr<storage::ShareableFileReference> platform_file,
151     base::File::Error error) {
152   DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
153   base::FilePath platform_path(platform_file.get()->path());
154   if (error != base::File::FILE_OK)
155     platform_file.reset();
156   std::move(callback).Run(error, file_info, platform_path, platform_file);
157 }
158 
159 // Called when the snapshot file specified by the |platform_path| is
160 // successfully created. |file_info| contains the device media file details
161 // for which the snapshot file is created.
OnDidCreateSnapshotFile(AsyncFileUtil::CreateSnapshotFileCallback callback,base::SequencedTaskRunner * media_task_runner,bool validate_media_files,const base::File::Info & file_info,const base::FilePath & platform_path)162 void OnDidCreateSnapshotFile(AsyncFileUtil::CreateSnapshotFileCallback callback,
163                              base::SequencedTaskRunner* media_task_runner,
164                              bool validate_media_files,
165                              const base::File::Info& file_info,
166                              const base::FilePath& platform_path) {
167   DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
168   scoped_refptr<storage::ShareableFileReference> file =
169       ShareableFileReference::GetOrCreate(
170           platform_path,
171           ShareableFileReference::DELETE_ON_FINAL_RELEASE,
172           media_task_runner);
173 
174   if (validate_media_files) {
175     base::PostTaskAndReplyWithResult(
176         media_task_runner, FROM_HERE,
177         base::BindOnce(&NativeMediaFileUtil::IsMediaFile, platform_path),
178         base::BindOnce(&OnDidCheckMediaForCreateSnapshotFile,
179                        std::move(callback), file_info, file));
180   } else {
181     OnDidCheckMediaForCreateSnapshotFile(std::move(callback), file_info, file,
182                                          base::File::FILE_OK);
183   }
184 }
185 
186 // Called when CreateSnapshotFile method call fails. |callback| is invoked to
187 // notify the caller about the |error|.
OnCreateSnapshotFileError(AsyncFileUtil::CreateSnapshotFileCallback callback,base::File::Error error)188 void OnCreateSnapshotFileError(
189     AsyncFileUtil::CreateSnapshotFileCallback callback,
190     base::File::Error error) {
191   DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
192   std::move(callback).Run(error, base::File::Info(), base::FilePath(),
193                           scoped_refptr<ShareableFileReference>());
194 }
195 
196 // Called when the snapshot file specified by the |snapshot_file_path| is
197 // created to hold the contents of the url.path(). If the snapshot
198 // file is successfully created, |snapshot_file_path| will be an non-empty
199 // file path. In case of failure, |snapshot_file_path| will be an empty file
200 // path. Forwards the CreateSnapshot request to the delegate to copy the
201 // contents of url.path() to |snapshot_file_path|.
OnSnapshotFileCreatedRunTask(std::unique_ptr<FileSystemOperationContext> context,AsyncFileUtil::CreateSnapshotFileCallback callback,const FileSystemURL & url,bool validate_media_files,const base::FilePath & snapshot_file_path)202 void OnSnapshotFileCreatedRunTask(
203     std::unique_ptr<FileSystemOperationContext> context,
204     AsyncFileUtil::CreateSnapshotFileCallback callback,
205     const FileSystemURL& url,
206     bool validate_media_files,
207     const base::FilePath& snapshot_file_path) {
208   DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
209   if (snapshot_file_path.empty()) {
210     OnCreateSnapshotFileError(std::move(callback),
211                               base::File::FILE_ERROR_FAILED);
212     return;
213   }
214   MTPDeviceAsyncDelegate* delegate =
215       MTPDeviceMapService::GetInstance()->GetMTPDeviceAsyncDelegate(url);
216   if (!delegate) {
217     OnCreateSnapshotFileError(std::move(callback),
218                               base::File::FILE_ERROR_NOT_FOUND);
219     return;
220   }
221   auto copyable_callback = base::AdaptCallbackForRepeating(std::move(callback));
222   delegate->CreateSnapshotFile(
223       url.path(),  // device file path
224       snapshot_file_path,
225       base::Bind(&OnDidCreateSnapshotFile, copyable_callback,
226                  base::RetainedRef(context->task_runner()),
227                  validate_media_files),
228       base::Bind(&OnCreateSnapshotFileError, copyable_callback));
229 }
230 
231 }  // namespace
232 
233 class DeviceMediaAsyncFileUtil::MediaPathFilterWrapper
234     : public base::RefCountedThreadSafe<MediaPathFilterWrapper> {
235  public:
236   MediaPathFilterWrapper();
237 
238   // Check if entries in |file_list| look like media files.
239   // Append the ones that look like media files to |results|.
240   // Should run on a media task runner.
241   AsyncFileUtil::EntryList FilterMediaEntries(
242       const AsyncFileUtil::EntryList& file_list);
243 
244   // Check if |path| looks like a media file.
245   bool CheckFilePath(const base::FilePath& path);
246 
247  private:
248   friend class base::RefCountedThreadSafe<MediaPathFilterWrapper>;
249 
250   virtual ~MediaPathFilterWrapper();
251 
252   std::unique_ptr<MediaPathFilter> media_path_filter_;
253 
254   DISALLOW_COPY_AND_ASSIGN(MediaPathFilterWrapper);
255 };
256 
MediaPathFilterWrapper()257 DeviceMediaAsyncFileUtil::MediaPathFilterWrapper::MediaPathFilterWrapper()
258     : media_path_filter_(new MediaPathFilter) {
259 }
260 
~MediaPathFilterWrapper()261 DeviceMediaAsyncFileUtil::MediaPathFilterWrapper::~MediaPathFilterWrapper() {
262 }
263 
264 AsyncFileUtil::EntryList
FilterMediaEntries(const AsyncFileUtil::EntryList & file_list)265 DeviceMediaAsyncFileUtil::MediaPathFilterWrapper::FilterMediaEntries(
266     const AsyncFileUtil::EntryList& file_list) {
267   AsyncFileUtil::EntryList results;
268   for (size_t i = 0; i < file_list.size(); ++i) {
269     const filesystem::mojom::DirectoryEntry& entry = file_list[i];
270     if (entry.type == filesystem::mojom::FsFileType::DIRECTORY ||
271         CheckFilePath(entry.name)) {
272       results.push_back(entry);
273     }
274   }
275   return results;
276 }
277 
CheckFilePath(const base::FilePath & path)278 bool DeviceMediaAsyncFileUtil::MediaPathFilterWrapper::CheckFilePath(
279     const base::FilePath& path) {
280   return media_path_filter_->Match(path);
281 }
282 
~DeviceMediaAsyncFileUtil()283 DeviceMediaAsyncFileUtil::~DeviceMediaAsyncFileUtil() {
284 }
285 
286 // static
Create(const base::FilePath & profile_path,MediaFileValidationType validation_type)287 std::unique_ptr<DeviceMediaAsyncFileUtil> DeviceMediaAsyncFileUtil::Create(
288     const base::FilePath& profile_path,
289     MediaFileValidationType validation_type) {
290   DCHECK(!profile_path.empty());
291   return base::WrapUnique(
292       new DeviceMediaAsyncFileUtil(profile_path, validation_type));
293 }
294 
SupportsStreaming(const storage::FileSystemURL & url)295 bool DeviceMediaAsyncFileUtil::SupportsStreaming(
296     const storage::FileSystemURL& url) {
297   DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
298   MTPDeviceAsyncDelegate* delegate =
299       MTPDeviceMapService::GetInstance()->GetMTPDeviceAsyncDelegate(url);
300   if (!delegate)
301     return false;
302   return delegate->IsStreaming();
303 }
304 
CreateOrOpen(std::unique_ptr<FileSystemOperationContext> context,const FileSystemURL & url,int file_flags,CreateOrOpenCallback callback)305 void DeviceMediaAsyncFileUtil::CreateOrOpen(
306     std::unique_ptr<FileSystemOperationContext> context,
307     const FileSystemURL& url,
308     int file_flags,
309     CreateOrOpenCallback callback) {
310   DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
311   // Returns an error if any unsupported flag is found.
312   if (file_flags & ~(base::File::FLAG_OPEN |
313                      base::File::FLAG_READ |
314                      base::File::FLAG_WRITE_ATTRIBUTES)) {
315     std::move(callback).Run(base::File(base::File::FILE_ERROR_SECURITY),
316                             base::Closure());
317     return;
318   }
319   auto* task_runner = context->task_runner();
320   CreateSnapshotFile(
321       std::move(context), url,
322       base::BindOnce(&NativeMediaFileUtil::CreatedSnapshotFileForCreateOrOpen,
323                      base::RetainedRef(task_runner), file_flags,
324                      std::move(callback)));
325 }
326 
EnsureFileExists(std::unique_ptr<FileSystemOperationContext> context,const FileSystemURL & url,EnsureFileExistsCallback callback)327 void DeviceMediaAsyncFileUtil::EnsureFileExists(
328     std::unique_ptr<FileSystemOperationContext> context,
329     const FileSystemURL& url,
330     EnsureFileExistsCallback callback) {
331   DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
332   NOTIMPLEMENTED();
333   std::move(callback).Run(base::File::FILE_ERROR_SECURITY, false);
334 }
335 
CreateDirectory(std::unique_ptr<FileSystemOperationContext> context,const FileSystemURL & url,bool exclusive,bool recursive,StatusCallback callback)336 void DeviceMediaAsyncFileUtil::CreateDirectory(
337     std::unique_ptr<FileSystemOperationContext> context,
338     const FileSystemURL& url,
339     bool exclusive,
340     bool recursive,
341     StatusCallback callback) {
342   DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
343   MTPDeviceAsyncDelegate* delegate =
344       MTPDeviceMapService::GetInstance()->GetMTPDeviceAsyncDelegate(url);
345   if (!delegate) {
346     OnCreateDirectoryError(std::move(callback),
347                            base::File::FILE_ERROR_NOT_FOUND);
348     return;
349   }
350   if (delegate->IsReadOnly()) {
351     OnCreateDirectoryError(std::move(callback),
352                            base::File::FILE_ERROR_SECURITY);
353     return;
354   }
355   auto copyable_callback = base::AdaptCallbackForRepeating(std::move(callback));
356   delegate->CreateDirectory(
357       url.path(), exclusive, recursive,
358       base::Bind(&DeviceMediaAsyncFileUtil::OnDidCreateDirectory,
359                  weak_ptr_factory_.GetWeakPtr(), copyable_callback),
360       base::Bind(&OnCreateDirectoryError, copyable_callback));
361 }
362 
GetFileInfo(std::unique_ptr<FileSystemOperationContext> context,const FileSystemURL & url,int,GetFileInfoCallback callback)363 void DeviceMediaAsyncFileUtil::GetFileInfo(
364     std::unique_ptr<FileSystemOperationContext> context,
365     const FileSystemURL& url,
366     int /* flags */,
367     GetFileInfoCallback callback) {
368   DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
369   MTPDeviceAsyncDelegate* delegate =
370       MTPDeviceMapService::GetInstance()->GetMTPDeviceAsyncDelegate(url);
371   if (!delegate) {
372     OnGetFileInfoError(std::move(callback), base::File::FILE_ERROR_NOT_FOUND);
373     return;
374   }
375   auto copyable_callback = base::AdaptCallbackForRepeating(std::move(callback));
376   delegate->GetFileInfo(url.path(),
377                         base::Bind(&DeviceMediaAsyncFileUtil::OnDidGetFileInfo,
378                                    weak_ptr_factory_.GetWeakPtr(),
379                                    base::RetainedRef(context->task_runner()),
380                                    url.path(), copyable_callback),
381                         base::Bind(&OnGetFileInfoError, copyable_callback));
382 }
383 
ReadDirectory(std::unique_ptr<FileSystemOperationContext> context,const FileSystemURL & url,ReadDirectoryCallback callback)384 void DeviceMediaAsyncFileUtil::ReadDirectory(
385     std::unique_ptr<FileSystemOperationContext> context,
386     const FileSystemURL& url,
387     ReadDirectoryCallback callback) {
388   DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
389   MTPDeviceAsyncDelegate* delegate =
390       MTPDeviceMapService::GetInstance()->GetMTPDeviceAsyncDelegate(url);
391   if (!delegate) {
392     OnReadDirectoryError(callback, base::File::FILE_ERROR_NOT_FOUND);
393     return;
394   }
395 
396   delegate->ReadDirectory(
397       url.path(),
398       base::Bind(&DeviceMediaAsyncFileUtil::OnDidReadDirectory,
399                  weak_ptr_factory_.GetWeakPtr(),
400                  base::RetainedRef(context->task_runner()), callback),
401       base::Bind(&OnReadDirectoryError, callback));
402 }
403 
Touch(std::unique_ptr<FileSystemOperationContext> context,const FileSystemURL & url,const base::Time & last_access_time,const base::Time & last_modified_time,StatusCallback callback)404 void DeviceMediaAsyncFileUtil::Touch(
405     std::unique_ptr<FileSystemOperationContext> context,
406     const FileSystemURL& url,
407     const base::Time& last_access_time,
408     const base::Time& last_modified_time,
409     StatusCallback callback) {
410   DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
411   NOTIMPLEMENTED();
412   std::move(callback).Run(base::File::FILE_ERROR_SECURITY);
413 }
414 
Truncate(std::unique_ptr<FileSystemOperationContext> context,const FileSystemURL & url,int64_t length,StatusCallback callback)415 void DeviceMediaAsyncFileUtil::Truncate(
416     std::unique_ptr<FileSystemOperationContext> context,
417     const FileSystemURL& url,
418     int64_t length,
419     StatusCallback callback) {
420   DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
421   NOTIMPLEMENTED();
422   std::move(callback).Run(base::File::FILE_ERROR_SECURITY);
423 }
424 
CopyFileLocal(std::unique_ptr<FileSystemOperationContext> context,const FileSystemURL & src_url,const FileSystemURL & dest_url,CopyOrMoveOption option,CopyFileProgressCallback progress_callback,StatusCallback callback)425 void DeviceMediaAsyncFileUtil::CopyFileLocal(
426     std::unique_ptr<FileSystemOperationContext> context,
427     const FileSystemURL& src_url,
428     const FileSystemURL& dest_url,
429     CopyOrMoveOption option,
430     CopyFileProgressCallback progress_callback,
431     StatusCallback callback) {
432   DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
433 
434   MTPDeviceAsyncDelegate* delegate =
435       MTPDeviceMapService::GetInstance()->GetMTPDeviceAsyncDelegate(dest_url);
436   if (!delegate) {
437     OnCopyFileLocalError(std::move(callback), base::File::FILE_ERROR_NOT_FOUND);
438     return;
439   }
440   if (delegate->IsReadOnly()) {
441     OnCopyFileLocalError(std::move(callback), base::File::FILE_ERROR_SECURITY);
442     return;
443   }
444 
445   auto copyable_callback = base::AdaptCallbackForRepeating(std::move(callback));
446   delegate->CopyFileLocal(
447       src_url.path(), dest_url.path(),
448       base::Bind(&CreateSnapshotFileOnBlockingPool, profile_path_),
449       progress_callback,
450       base::Bind(&DeviceMediaAsyncFileUtil::OnDidCopyFileLocal,
451                  weak_ptr_factory_.GetWeakPtr(), copyable_callback),
452       base::Bind(&OnCopyFileLocalError, copyable_callback));
453 }
454 
MoveFileLocal(std::unique_ptr<FileSystemOperationContext> context,const FileSystemURL & src_url,const FileSystemURL & dest_url,CopyOrMoveOption option,StatusCallback callback)455 void DeviceMediaAsyncFileUtil::MoveFileLocal(
456     std::unique_ptr<FileSystemOperationContext> context,
457     const FileSystemURL& src_url,
458     const FileSystemURL& dest_url,
459     CopyOrMoveOption option,
460     StatusCallback callback) {
461   DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
462 
463   MTPDeviceAsyncDelegate* delegate =
464       MTPDeviceMapService::GetInstance()->GetMTPDeviceAsyncDelegate(dest_url);
465   if (!delegate) {
466     OnMoveFileLocalError(std::move(callback), base::File::FILE_ERROR_NOT_FOUND);
467     return;
468   }
469   if (delegate->IsReadOnly()) {
470     OnMoveFileLocalError(std::move(callback), base::File::FILE_ERROR_SECURITY);
471     return;
472   }
473 
474   auto copyable_callback = base::AdaptCallbackForRepeating(std::move(callback));
475   delegate->MoveFileLocal(
476       src_url.path(), dest_url.path(),
477       base::Bind(&CreateSnapshotFileOnBlockingPool, profile_path_),
478       base::Bind(&DeviceMediaAsyncFileUtil::OnDidMoveFileLocal,
479                  weak_ptr_factory_.GetWeakPtr(), copyable_callback),
480       base::Bind(&OnMoveFileLocalError, copyable_callback));
481 }
482 
CopyInForeignFile(std::unique_ptr<FileSystemOperationContext> context,const base::FilePath & src_file_path,const FileSystemURL & dest_url,StatusCallback callback)483 void DeviceMediaAsyncFileUtil::CopyInForeignFile(
484     std::unique_ptr<FileSystemOperationContext> context,
485     const base::FilePath& src_file_path,
486     const FileSystemURL& dest_url,
487     StatusCallback callback) {
488   DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
489 
490   MTPDeviceAsyncDelegate* delegate =
491       MTPDeviceMapService::GetInstance()->GetMTPDeviceAsyncDelegate(dest_url);
492   if (!delegate) {
493     OnCopyInForeignFileError(std::move(callback),
494                              base::File::FILE_ERROR_NOT_FOUND);
495     return;
496   }
497   if (delegate->IsReadOnly()) {
498     OnCopyInForeignFileError(std::move(callback),
499                              base::File::FILE_ERROR_SECURITY);
500     return;
501   }
502 
503   auto copyable_callback = base::AdaptCallbackForRepeating(std::move(callback));
504   delegate->CopyFileFromLocal(
505       src_file_path, dest_url.path(),
506       base::Bind(&DeviceMediaAsyncFileUtil::OnDidCopyInForeignFile,
507                  weak_ptr_factory_.GetWeakPtr(), copyable_callback),
508       base::Bind(&OnCopyInForeignFileError, copyable_callback));
509 }
510 
DeleteFile(std::unique_ptr<FileSystemOperationContext> context,const FileSystemURL & url,StatusCallback callback)511 void DeviceMediaAsyncFileUtil::DeleteFile(
512     std::unique_ptr<FileSystemOperationContext> context,
513     const FileSystemURL& url,
514     StatusCallback callback) {
515   DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
516 
517   MTPDeviceAsyncDelegate* const delegate =
518       MTPDeviceMapService::GetInstance()->GetMTPDeviceAsyncDelegate(url);
519   if (!delegate) {
520     OnDeleteFileError(std::move(callback), base::File::FILE_ERROR_NOT_FOUND);
521     return;
522   }
523   if (delegate->IsReadOnly()) {
524     OnDeleteFileError(std::move(callback), base::File::FILE_ERROR_SECURITY);
525     return;
526   }
527 
528   auto copyable_callback = base::AdaptCallbackForRepeating(std::move(callback));
529   delegate->DeleteFile(
530       url.path(),
531       base::Bind(&DeviceMediaAsyncFileUtil::OnDidDeleteFile,
532                  weak_ptr_factory_.GetWeakPtr(), copyable_callback),
533       base::Bind(&OnDeleteFileError, copyable_callback));
534 }
535 
DeleteDirectory(std::unique_ptr<FileSystemOperationContext> context,const FileSystemURL & url,StatusCallback callback)536 void DeviceMediaAsyncFileUtil::DeleteDirectory(
537     std::unique_ptr<FileSystemOperationContext> context,
538     const FileSystemURL& url,
539     StatusCallback callback) {
540   DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
541 
542   MTPDeviceAsyncDelegate* const delegate =
543       MTPDeviceMapService::GetInstance()->GetMTPDeviceAsyncDelegate(url);
544   if (!delegate) {
545     OnDeleteDirectoryError(std::move(callback),
546                            base::File::FILE_ERROR_NOT_FOUND);
547     return;
548   }
549   if (delegate->IsReadOnly()) {
550     OnDeleteDirectoryError(std::move(callback),
551                            base::File::FILE_ERROR_SECURITY);
552     return;
553   }
554 
555   auto copyable_callback = base::AdaptCallbackForRepeating(std::move(callback));
556   delegate->DeleteDirectory(
557       url.path(),
558       base::Bind(&DeviceMediaAsyncFileUtil::OnDidDeleteDirectory,
559                  weak_ptr_factory_.GetWeakPtr(), copyable_callback),
560       base::Bind(&OnDeleteDirectoryError, copyable_callback));
561 }
562 
DeleteRecursively(std::unique_ptr<FileSystemOperationContext> context,const FileSystemURL & url,StatusCallback callback)563 void DeviceMediaAsyncFileUtil::DeleteRecursively(
564     std::unique_ptr<FileSystemOperationContext> context,
565     const FileSystemURL& url,
566     StatusCallback callback) {
567   DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
568   std::move(callback).Run(base::File::FILE_ERROR_INVALID_OPERATION);
569 }
570 
CreateSnapshotFile(std::unique_ptr<FileSystemOperationContext> context,const FileSystemURL & url,CreateSnapshotFileCallback callback)571 void DeviceMediaAsyncFileUtil::CreateSnapshotFile(
572     std::unique_ptr<FileSystemOperationContext> context,
573     const FileSystemURL& url,
574     CreateSnapshotFileCallback callback) {
575   DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
576   MTPDeviceAsyncDelegate* delegate =
577       MTPDeviceMapService::GetInstance()->GetMTPDeviceAsyncDelegate(url);
578   if (!delegate) {
579     OnCreateSnapshotFileError(std::move(callback),
580                               base::File::FILE_ERROR_NOT_FOUND);
581     return;
582   }
583 
584   scoped_refptr<base::SequencedTaskRunner> task_runner(context->task_runner());
585   base::PostTaskAndReplyWithResult(
586       task_runner.get(), FROM_HERE,
587       base::BindOnce(&CreateSnapshotFileOnBlockingPool, profile_path_),
588       base::BindOnce(&OnSnapshotFileCreatedRunTask, std::move(context),
589                      std::move(callback), url, validate_media_files()));
590 }
591 
592 std::unique_ptr<storage::FileStreamReader>
GetFileStreamReader(const FileSystemURL & url,int64_t offset,const base::Time & expected_modification_time,storage::FileSystemContext * context)593 DeviceMediaAsyncFileUtil::GetFileStreamReader(
594     const FileSystemURL& url,
595     int64_t offset,
596     const base::Time& expected_modification_time,
597     storage::FileSystemContext* context) {
598   MTPDeviceAsyncDelegate* delegate =
599       MTPDeviceMapService::GetInstance()->GetMTPDeviceAsyncDelegate(url);
600   if (!delegate)
601     return std::unique_ptr<storage::FileStreamReader>();
602 
603   DCHECK(delegate->IsStreaming());
604   return std::unique_ptr<storage::FileStreamReader>(
605       new ReadaheadFileStreamReader(new MTPFileStreamReader(
606           context, url, offset, expected_modification_time,
607           validate_media_files())));
608 }
609 
AddWatcher(const storage::FileSystemURL & url,bool recursive,storage::WatcherManager::StatusCallback callback,storage::WatcherManager::NotificationCallback notification_callback)610 void DeviceMediaAsyncFileUtil::AddWatcher(
611     const storage::FileSystemURL& url,
612     bool recursive,
613     storage::WatcherManager::StatusCallback callback,
614     storage::WatcherManager::NotificationCallback notification_callback) {
615   MTPDeviceAsyncDelegate* const delegate =
616       MTPDeviceMapService::GetInstance()->GetMTPDeviceAsyncDelegate(url);
617   if (!delegate) {
618     std::move(callback).Run(base::File::FILE_ERROR_FAILED);
619     return;
620   }
621 
622   delegate->AddWatcher(url.origin().GetURL(), url.path(), recursive,
623                        std::move(callback), std::move(notification_callback));
624 }
625 
RemoveWatcher(const storage::FileSystemURL & url,const bool recursive,storage::WatcherManager::StatusCallback callback)626 void DeviceMediaAsyncFileUtil::RemoveWatcher(
627     const storage::FileSystemURL& url,
628     const bool recursive,
629     storage::WatcherManager::StatusCallback callback) {
630   MTPDeviceAsyncDelegate* const delegate =
631       MTPDeviceMapService::GetInstance()->GetMTPDeviceAsyncDelegate(url);
632   if (!delegate) {
633     std::move(callback).Run(base::File::FILE_ERROR_FAILED);
634     return;
635   }
636 
637   delegate->RemoveWatcher(url.origin().GetURL(), url.path(), recursive,
638                           std::move(callback));
639 }
640 
DeviceMediaAsyncFileUtil(const base::FilePath & profile_path,MediaFileValidationType validation_type)641 DeviceMediaAsyncFileUtil::DeviceMediaAsyncFileUtil(
642     const base::FilePath& profile_path,
643     MediaFileValidationType validation_type)
644     : profile_path_(profile_path) {
645   if (validation_type == APPLY_MEDIA_FILE_VALIDATION) {
646     media_path_filter_wrapper_ = new MediaPathFilterWrapper;
647   }
648 }
649 
OnDidCreateDirectory(StatusCallback callback)650 void DeviceMediaAsyncFileUtil::OnDidCreateDirectory(StatusCallback callback) {
651   DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
652 
653   std::move(callback).Run(base::File::FILE_OK);
654 }
655 
OnDidGetFileInfo(base::SequencedTaskRunner * task_runner,const base::FilePath & path,AsyncFileUtil::GetFileInfoCallback callback,const base::File::Info & file_info)656 void DeviceMediaAsyncFileUtil::OnDidGetFileInfo(
657     base::SequencedTaskRunner* task_runner,
658     const base::FilePath& path,
659     AsyncFileUtil::GetFileInfoCallback callback,
660     const base::File::Info& file_info) {
661   DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
662   if (file_info.is_directory || !validate_media_files()) {
663     OnDidCheckMediaForGetFileInfo(std::move(callback), file_info,
664                                   true /* valid */);
665     return;
666   }
667 
668   base::PostTaskAndReplyWithResult(
669       task_runner, FROM_HERE,
670       base::BindOnce(&MediaPathFilterWrapper::CheckFilePath,
671                      media_path_filter_wrapper_, path),
672       base::BindOnce(&OnDidCheckMediaForGetFileInfo, std::move(callback),
673                      file_info));
674 }
675 
OnDidReadDirectory(base::SequencedTaskRunner * task_runner,AsyncFileUtil::ReadDirectoryCallback callback,AsyncFileUtil::EntryList file_list,bool has_more)676 void DeviceMediaAsyncFileUtil::OnDidReadDirectory(
677     base::SequencedTaskRunner* task_runner,
678     AsyncFileUtil::ReadDirectoryCallback callback,
679     AsyncFileUtil::EntryList file_list,
680     bool has_more) {
681   DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
682   if (!validate_media_files()) {
683     OnDidCheckMediaForReadDirectory(callback, has_more, std::move(file_list));
684     return;
685   }
686 
687   base::PostTaskAndReplyWithResult(
688       task_runner, FROM_HERE,
689       base::BindOnce(&MediaPathFilterWrapper::FilterMediaEntries,
690                      media_path_filter_wrapper_, std::move(file_list)),
691       base::BindOnce(&OnDidCheckMediaForReadDirectory, callback, has_more));
692 }
693 
OnDidCopyFileLocal(StatusCallback callback)694 void DeviceMediaAsyncFileUtil::OnDidCopyFileLocal(StatusCallback callback) {
695   DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
696 
697   std::move(callback).Run(base::File::FILE_OK);
698 }
699 
OnDidMoveFileLocal(StatusCallback callback)700 void DeviceMediaAsyncFileUtil::OnDidMoveFileLocal(StatusCallback callback) {
701   DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
702 
703   std::move(callback).Run(base::File::FILE_OK);
704 }
705 
OnDidCopyInForeignFile(StatusCallback callback)706 void DeviceMediaAsyncFileUtil::OnDidCopyInForeignFile(StatusCallback callback) {
707   DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
708 
709   std::move(callback).Run(base::File::FILE_OK);
710 }
711 
OnDidDeleteFile(StatusCallback callback)712 void DeviceMediaAsyncFileUtil::OnDidDeleteFile(StatusCallback callback) {
713   DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
714 
715   std::move(callback).Run(base::File::FILE_OK);
716 }
717 
OnDidDeleteDirectory(StatusCallback callback)718 void DeviceMediaAsyncFileUtil::OnDidDeleteDirectory(StatusCallback callback) {
719   DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
720 
721   std::move(callback).Run(base::File::FILE_OK);
722 }
723 
validate_media_files() const724 bool DeviceMediaAsyncFileUtil::validate_media_files() const {
725   return media_path_filter_wrapper_.get() != NULL;
726 }
727