| // Copyright 2013 The Chromium Authors |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "chrome/browser/media_galleries/fileapi/device_media_async_file_util.h" |
| |
| #include <stddef.h> |
| |
| #include <utility> |
| |
| #include "base/files/file_util.h" |
| #include "base/functional/bind.h" |
| #include "base/functional/callback.h" |
| #include "base/functional/callback_helpers.h" |
| #include "base/memory/ptr_util.h" |
| #include "base/task/sequenced_task_runner.h" |
| #include "base/task/single_thread_task_runner.h" |
| #include "chrome/browser/media_galleries/fileapi/media_path_filter.h" |
| #include "chrome/browser/media_galleries/fileapi/mtp_device_async_delegate.h" |
| #include "chrome/browser/media_galleries/fileapi/mtp_device_map_service.h" |
| #include "chrome/browser/media_galleries/fileapi/mtp_file_stream_reader.h" |
| #include "chrome/browser/media_galleries/fileapi/native_media_file_util.h" |
| #include "chrome/browser/media_galleries/fileapi/readahead_file_stream_reader.h" |
| #include "components/services/filesystem/public/mojom/types.mojom.h" |
| #include "content/public/browser/browser_thread.h" |
| #include "storage/browser/blob/shareable_file_reference.h" |
| #include "storage/browser/file_system/file_stream_reader.h" |
| #include "storage/browser/file_system/file_system_context.h" |
| #include "storage/browser/file_system/file_system_operation_context.h" |
| #include "storage/browser/file_system/file_system_url.h" |
| #include "storage/browser/file_system/native_file_util.h" |
| |
| using storage::AsyncFileUtil; |
| using storage::FileSystemOperationContext; |
| using storage::FileSystemURL; |
| using storage::ShareableFileReference; |
| |
| namespace { |
| |
| const char kDeviceMediaAsyncFileUtilTempDir[] = "DeviceMediaFileSystem"; |
| |
| // Called when GetFileInfo method call failed to get the details of file |
| // specified by the requested url. |callback| is invoked to notify the |
| // caller about the file |error|. |
| void OnGetFileInfoError(AsyncFileUtil::GetFileInfoCallback callback, |
| base::File::Error error) { |
| DCHECK_CURRENTLY_ON(content::BrowserThread::IO); |
| std::move(callback).Run(error, base::File::Info()); |
| } |
| |
| // Called after OnDidGetFileInfo finishes media check. |
| // |callback| is invoked to complete the GetFileInfo request. |
| void OnDidCheckMediaForGetFileInfo(AsyncFileUtil::GetFileInfoCallback callback, |
| const base::File::Info& file_info, |
| bool is_valid_file) { |
| DCHECK_CURRENTLY_ON(content::BrowserThread::IO); |
| if (!is_valid_file) { |
| OnGetFileInfoError(std::move(callback), base::File::FILE_ERROR_NOT_FOUND); |
| return; |
| } |
| std::move(callback).Run(base::File::FILE_OK, file_info); |
| } |
| |
| // Called after OnDidReadDirectory finishes media check. |
| // |callback| is invoked to complete the ReadDirectory request. |
| void OnDidCheckMediaForReadDirectory( |
| AsyncFileUtil::ReadDirectoryCallback callback, |
| bool has_more, |
| AsyncFileUtil::EntryList file_list) { |
| DCHECK_CURRENTLY_ON(content::BrowserThread::IO); |
| callback.Run(base::File::FILE_OK, std::move(file_list), has_more); |
| } |
| |
| // Called when CreateDirectory method call failed. |
| void OnCreateDirectoryError(AsyncFileUtil::StatusCallback callback, |
| base::File::Error error) { |
| DCHECK_CURRENTLY_ON(content::BrowserThread::IO); |
| std::move(callback).Run(error); |
| } |
| |
| // Called when ReadDirectory method call failed to enumerate the directory |
| // objects. |callback| is invoked to notify the caller about the |error| |
| // that occured while reading the directory objects. |
| void OnReadDirectoryError(AsyncFileUtil::ReadDirectoryCallback callback, |
| base::File::Error error) { |
| DCHECK_CURRENTLY_ON(content::BrowserThread::IO); |
| callback.Run(error, AsyncFileUtil::EntryList(), false /*no more*/); |
| } |
| |
| // Called when CopyFileLocal method call failed. |
| void OnCopyFileLocalError(AsyncFileUtil::StatusCallback callback, |
| base::File::Error error) { |
| DCHECK_CURRENTLY_ON(content::BrowserThread::IO); |
| std::move(callback).Run(error); |
| } |
| |
| // Called when MoveFileLocal method call failed. |
| void OnMoveFileLocalError(AsyncFileUtil::StatusCallback callback, |
| base::File::Error error) { |
| DCHECK_CURRENTLY_ON(content::BrowserThread::IO); |
| std::move(callback).Run(error); |
| } |
| |
| // Called when CopyInForeignFile method call failed. |
| void OnCopyInForeignFileError(AsyncFileUtil::StatusCallback callback, |
| base::File::Error error) { |
| DCHECK_CURRENTLY_ON(content::BrowserThread::IO); |
| std::move(callback).Run(error); |
| } |
| |
| // Called when DeleteFile method call failed. |
| void OnDeleteFileError(AsyncFileUtil::StatusCallback callback, |
| base::File::Error error) { |
| DCHECK_CURRENTLY_ON(content::BrowserThread::IO); |
| std::move(callback).Run(error); |
| } |
| |
| // Called when DeleteDirectory method call failed. |
| void OnDeleteDirectoryError(AsyncFileUtil::StatusCallback callback, |
| base::File::Error error) { |
| DCHECK_CURRENTLY_ON(content::BrowserThread::IO); |
| std::move(callback).Run(error); |
| } |
| |
| // Called on a blocking pool thread to create a snapshot file to hold the |
| // contents of |device_file_path|. The snapshot file is created in the |
| // "profile_path/kDeviceMediaAsyncFileUtilTempDir" directory. Return the |
| // snapshot file path or an empty path on failure. |
| base::FilePath CreateSnapshotFileOnBlockingPool( |
| const base::FilePath& profile_path) { |
| base::FilePath snapshot_file_path; |
| base::FilePath media_file_system_dir_path = |
| profile_path.AppendASCII(kDeviceMediaAsyncFileUtilTempDir); |
| if (!base::CreateDirectory(media_file_system_dir_path) || |
| !base::CreateTemporaryFileInDir(media_file_system_dir_path, |
| &snapshot_file_path)) { |
| LOG(WARNING) << "Could not create media snapshot file " |
| << media_file_system_dir_path.value(); |
| snapshot_file_path = base::FilePath(); |
| } |
| return snapshot_file_path; |
| } |
| |
| // Called after OnDidCreateSnapshotFile finishes media check. |
| // |callback| is invoked to complete the CreateSnapshotFile request. |
| void OnDidCheckMediaForCreateSnapshotFile( |
| AsyncFileUtil::CreateSnapshotFileCallback callback, |
| const base::File::Info& file_info, |
| scoped_refptr<storage::ShareableFileReference> platform_file, |
| base::File::Error error) { |
| DCHECK_CURRENTLY_ON(content::BrowserThread::IO); |
| base::FilePath platform_path(platform_file.get()->path()); |
| if (error != base::File::FILE_OK) |
| platform_file.reset(); |
| std::move(callback).Run(error, file_info, platform_path, platform_file); |
| } |
| |
| // Called when the snapshot file specified by the |platform_path| is |
| // successfully created. |file_info| contains the device media file details |
| // for which the snapshot file is created. |
| void OnDidCreateSnapshotFile(AsyncFileUtil::CreateSnapshotFileCallback callback, |
| base::SequencedTaskRunner* media_task_runner, |
| bool validate_media_files, |
| const base::File::Info& file_info, |
| const base::FilePath& platform_path) { |
| DCHECK_CURRENTLY_ON(content::BrowserThread::IO); |
| scoped_refptr<storage::ShareableFileReference> file = |
| ShareableFileReference::GetOrCreate( |
| platform_path, |
| ShareableFileReference::DELETE_ON_FINAL_RELEASE, |
| media_task_runner); |
| |
| if (validate_media_files) { |
| media_task_runner->PostTaskAndReplyWithResult( |
| FROM_HERE, |
| base::BindOnce(&NativeMediaFileUtil::IsMediaFile, platform_path), |
| base::BindOnce(&OnDidCheckMediaForCreateSnapshotFile, |
| std::move(callback), file_info, file)); |
| } else { |
| OnDidCheckMediaForCreateSnapshotFile(std::move(callback), file_info, file, |
| base::File::FILE_OK); |
| } |
| } |
| |
| // Called when CreateSnapshotFile method call fails. |callback| is invoked to |
| // notify the caller about the |error|. |
| void OnCreateSnapshotFileError( |
| AsyncFileUtil::CreateSnapshotFileCallback callback, |
| base::File::Error error) { |
| DCHECK_CURRENTLY_ON(content::BrowserThread::IO); |
| std::move(callback).Run(error, base::File::Info(), base::FilePath(), |
| scoped_refptr<ShareableFileReference>()); |
| } |
| |
| // Called when the snapshot file specified by the |snapshot_file_path| is |
| // created to hold the contents of the url.path(). If the snapshot |
| // file is successfully created, |snapshot_file_path| will be an non-empty |
| // file path. In case of failure, |snapshot_file_path| will be an empty file |
| // path. Forwards the CreateSnapshot request to the delegate to copy the |
| // contents of url.path() to |snapshot_file_path|. |
| void OnSnapshotFileCreatedRunTask( |
| std::unique_ptr<FileSystemOperationContext> context, |
| AsyncFileUtil::CreateSnapshotFileCallback callback, |
| const FileSystemURL& url, |
| bool validate_media_files, |
| const base::FilePath& snapshot_file_path) { |
| DCHECK_CURRENTLY_ON(content::BrowserThread::IO); |
| if (snapshot_file_path.empty()) { |
| OnCreateSnapshotFileError(std::move(callback), |
| base::File::FILE_ERROR_FAILED); |
| return; |
| } |
| MTPDeviceAsyncDelegate* delegate = |
| MTPDeviceMapService::GetInstance()->GetMTPDeviceAsyncDelegate(url); |
| if (!delegate) { |
| OnCreateSnapshotFileError(std::move(callback), |
| base::File::FILE_ERROR_NOT_FOUND); |
| return; |
| } |
| |
| // Only one of the success or error callbacks will be called here. |
| auto split_callback = base::SplitOnceCallback(std::move(callback)); |
| delegate->CreateSnapshotFile( |
| url.path(), // device file path |
| snapshot_file_path, |
| base::BindOnce(&OnDidCreateSnapshotFile, std::move(split_callback.first), |
| base::RetainedRef(context->task_runner()), |
| validate_media_files), |
| base::BindOnce(&OnCreateSnapshotFileError, |
| std::move(split_callback.second))); |
| } |
| |
| } // namespace |
| |
| class DeviceMediaAsyncFileUtil::MediaPathFilterWrapper |
| : public base::RefCountedThreadSafe<MediaPathFilterWrapper> { |
| public: |
| MediaPathFilterWrapper(); |
| |
| MediaPathFilterWrapper(const MediaPathFilterWrapper&) = delete; |
| MediaPathFilterWrapper& operator=(const MediaPathFilterWrapper&) = delete; |
| |
| // Check if entries in |file_list| look like media files. |
| // Append the ones that look like media files to |results|. |
| // Should run on a media task runner. |
| AsyncFileUtil::EntryList FilterMediaEntries( |
| const AsyncFileUtil::EntryList& file_list); |
| |
| // Check if |path| looks like a media file. |
| bool CheckFilePath(const base::FilePath& path); |
| |
| private: |
| friend class base::RefCountedThreadSafe<MediaPathFilterWrapper>; |
| |
| virtual ~MediaPathFilterWrapper(); |
| |
| std::unique_ptr<MediaPathFilter> media_path_filter_; |
| }; |
| |
| DeviceMediaAsyncFileUtil::MediaPathFilterWrapper::MediaPathFilterWrapper() |
| : media_path_filter_(new MediaPathFilter) { |
| } |
| |
| DeviceMediaAsyncFileUtil::MediaPathFilterWrapper::~MediaPathFilterWrapper() { |
| } |
| |
| AsyncFileUtil::EntryList |
| DeviceMediaAsyncFileUtil::MediaPathFilterWrapper::FilterMediaEntries( |
| const AsyncFileUtil::EntryList& file_list) { |
| AsyncFileUtil::EntryList results; |
| for (size_t i = 0; i < file_list.size(); ++i) { |
| const filesystem::mojom::DirectoryEntry& entry = file_list[i]; |
| if (entry.type == filesystem::mojom::FsFileType::DIRECTORY || |
| CheckFilePath(entry.name)) { |
| results.push_back(entry); |
| } |
| } |
| return results; |
| } |
| |
| bool DeviceMediaAsyncFileUtil::MediaPathFilterWrapper::CheckFilePath( |
| const base::FilePath& path) { |
| return media_path_filter_->Match(path); |
| } |
| |
| DeviceMediaAsyncFileUtil::~DeviceMediaAsyncFileUtil() { |
| } |
| |
| // static |
| std::unique_ptr<DeviceMediaAsyncFileUtil> DeviceMediaAsyncFileUtil::Create( |
| const base::FilePath& profile_path, |
| MediaFileValidationType validation_type) { |
| DCHECK(!profile_path.empty()); |
| return base::WrapUnique( |
| new DeviceMediaAsyncFileUtil(profile_path, validation_type)); |
| } |
| |
| bool DeviceMediaAsyncFileUtil::SupportsStreaming( |
| const storage::FileSystemURL& url) { |
| DCHECK_CURRENTLY_ON(content::BrowserThread::IO); |
| MTPDeviceAsyncDelegate* delegate = |
| MTPDeviceMapService::GetInstance()->GetMTPDeviceAsyncDelegate(url); |
| if (!delegate) |
| return false; |
| return delegate->IsStreaming(); |
| } |
| |
| void DeviceMediaAsyncFileUtil::CreateOrOpen( |
| std::unique_ptr<FileSystemOperationContext> context, |
| const FileSystemURL& url, |
| uint32_t file_flags, |
| CreateOrOpenCallback callback) { |
| DCHECK_CURRENTLY_ON(content::BrowserThread::IO); |
| // Returns an error if any unsupported flag is found. |
| if (file_flags & |
| ~(base::File::FLAG_OPEN | base::File::FLAG_READ | |
| base::File::FLAG_WRITE_ATTRIBUTES | base::File::FLAG_WIN_NO_EXECUTE)) { |
| std::move(callback).Run(base::File(base::File::FILE_ERROR_SECURITY), |
| base::OnceClosure()); |
| return; |
| } |
| auto* task_runner = context->task_runner(); |
| CreateSnapshotFile( |
| std::move(context), url, |
| base::BindOnce(&NativeMediaFileUtil::CreatedSnapshotFileForCreateOrOpen, |
| base::RetainedRef(task_runner), file_flags, |
| std::move(callback))); |
| } |
| |
| void DeviceMediaAsyncFileUtil::EnsureFileExists( |
| std::unique_ptr<FileSystemOperationContext> context, |
| const FileSystemURL& url, |
| EnsureFileExistsCallback callback) { |
| DCHECK_CURRENTLY_ON(content::BrowserThread::IO); |
| NOTIMPLEMENTED(); |
| std::move(callback).Run(base::File::FILE_ERROR_SECURITY, false); |
| } |
| |
| void DeviceMediaAsyncFileUtil::CreateDirectory( |
| std::unique_ptr<FileSystemOperationContext> context, |
| const FileSystemURL& url, |
| bool exclusive, |
| bool recursive, |
| StatusCallback callback) { |
| DCHECK_CURRENTLY_ON(content::BrowserThread::IO); |
| MTPDeviceAsyncDelegate* delegate = |
| MTPDeviceMapService::GetInstance()->GetMTPDeviceAsyncDelegate(url); |
| if (!delegate) { |
| OnCreateDirectoryError(std::move(callback), |
| base::File::FILE_ERROR_NOT_FOUND); |
| return; |
| } |
| if (delegate->IsReadOnly()) { |
| OnCreateDirectoryError(std::move(callback), |
| base::File::FILE_ERROR_SECURITY); |
| return; |
| } |
| |
| // Only one of the success or error callbacks will be called here. |
| auto [on_success, on_error] = base::SplitOnceCallback(std::move(callback)); |
| delegate->CreateDirectory( |
| url.path(), exclusive, recursive, |
| base::BindOnce(&DeviceMediaAsyncFileUtil::OnDidCreateDirectory, |
| weak_ptr_factory_.GetWeakPtr(), std::move(on_success)), |
| base::BindOnce(&OnCreateDirectoryError, std::move(on_error))); |
| } |
| |
| void DeviceMediaAsyncFileUtil::GetFileInfo( |
| std::unique_ptr<FileSystemOperationContext> context, |
| const FileSystemURL& url, |
| GetMetadataFieldSet fields, |
| GetFileInfoCallback callback) { |
| DCHECK_CURRENTLY_ON(content::BrowserThread::IO); |
| MTPDeviceAsyncDelegate* delegate = |
| MTPDeviceMapService::GetInstance()->GetMTPDeviceAsyncDelegate(url); |
| if (!delegate) { |
| OnGetFileInfoError(std::move(callback), base::File::FILE_ERROR_NOT_FOUND); |
| return; |
| } |
| |
| // Only one of the success or error callbacks will be called here. |
| auto split_callback = base::SplitOnceCallback(std::move(callback)); |
| delegate->GetFileInfo( |
| url.path(), |
| base::BindOnce(&DeviceMediaAsyncFileUtil::OnDidGetFileInfo, |
| weak_ptr_factory_.GetWeakPtr(), |
| base::RetainedRef(context->task_runner()), url.path(), |
| std::move(split_callback.first)), |
| base::BindOnce(&OnGetFileInfoError, std::move(split_callback.second))); |
| } |
| |
| void DeviceMediaAsyncFileUtil::ReadDirectory( |
| std::unique_ptr<FileSystemOperationContext> context, |
| const FileSystemURL& url, |
| ReadDirectoryCallback callback) { |
| DCHECK_CURRENTLY_ON(content::BrowserThread::IO); |
| MTPDeviceAsyncDelegate* delegate = |
| MTPDeviceMapService::GetInstance()->GetMTPDeviceAsyncDelegate(url); |
| if (!delegate) { |
| OnReadDirectoryError(callback, base::File::FILE_ERROR_NOT_FOUND); |
| return; |
| } |
| |
| delegate->ReadDirectory( |
| url.path(), |
| base::BindRepeating(&DeviceMediaAsyncFileUtil::OnDidReadDirectory, |
| weak_ptr_factory_.GetWeakPtr(), |
| base::RetainedRef(context->task_runner()), callback), |
| base::BindOnce(&OnReadDirectoryError, callback)); |
| } |
| |
| void DeviceMediaAsyncFileUtil::Touch( |
| std::unique_ptr<FileSystemOperationContext> context, |
| const FileSystemURL& url, |
| const base::Time& last_access_time, |
| const base::Time& last_modified_time, |
| StatusCallback callback) { |
| DCHECK_CURRENTLY_ON(content::BrowserThread::IO); |
| NOTIMPLEMENTED(); |
| std::move(callback).Run(base::File::FILE_ERROR_SECURITY); |
| } |
| |
| void DeviceMediaAsyncFileUtil::Truncate( |
| std::unique_ptr<FileSystemOperationContext> context, |
| const FileSystemURL& url, |
| int64_t length, |
| StatusCallback callback) { |
| DCHECK_CURRENTLY_ON(content::BrowserThread::IO); |
| NOTIMPLEMENTED(); |
| std::move(callback).Run(base::File::FILE_ERROR_SECURITY); |
| } |
| |
| void DeviceMediaAsyncFileUtil::CopyFileLocal( |
| std::unique_ptr<FileSystemOperationContext> context, |
| const FileSystemURL& src_url, |
| const FileSystemURL& dest_url, |
| CopyOrMoveOptionSet options, |
| CopyFileProgressCallback progress_callback, |
| StatusCallback callback) { |
| DCHECK_CURRENTLY_ON(content::BrowserThread::IO); |
| |
| MTPDeviceAsyncDelegate* delegate = |
| MTPDeviceMapService::GetInstance()->GetMTPDeviceAsyncDelegate(dest_url); |
| if (!delegate) { |
| OnCopyFileLocalError(std::move(callback), base::File::FILE_ERROR_NOT_FOUND); |
| return; |
| } |
| if (delegate->IsReadOnly()) { |
| OnCopyFileLocalError(std::move(callback), base::File::FILE_ERROR_SECURITY); |
| return; |
| } |
| |
| // Only one of the success or error callbacks will be called here. |
| auto split_callback = base::SplitOnceCallback(std::move(callback)); |
| delegate->CopyFileLocal( |
| src_url.path(), dest_url.path(), |
| base::BindOnce(&CreateSnapshotFileOnBlockingPool, profile_path_), |
| progress_callback, |
| base::BindOnce(&DeviceMediaAsyncFileUtil::OnDidCopyFileLocal, |
| weak_ptr_factory_.GetWeakPtr(), |
| std::move(split_callback.first)), |
| base::BindOnce(&OnCopyFileLocalError, std::move(split_callback.second))); |
| } |
| |
| void DeviceMediaAsyncFileUtil::MoveFileLocal( |
| std::unique_ptr<FileSystemOperationContext> context, |
| const FileSystemURL& src_url, |
| const FileSystemURL& dest_url, |
| CopyOrMoveOptionSet options, |
| StatusCallback callback) { |
| DCHECK_CURRENTLY_ON(content::BrowserThread::IO); |
| |
| MTPDeviceAsyncDelegate* delegate = |
| MTPDeviceMapService::GetInstance()->GetMTPDeviceAsyncDelegate(dest_url); |
| if (!delegate) { |
| OnMoveFileLocalError(std::move(callback), base::File::FILE_ERROR_NOT_FOUND); |
| return; |
| } |
| if (delegate->IsReadOnly()) { |
| OnMoveFileLocalError(std::move(callback), base::File::FILE_ERROR_SECURITY); |
| return; |
| } |
| |
| // Only one of the success or error callbacks will be called here. |
| auto split_callback = base::SplitOnceCallback(std::move(callback)); |
| delegate->MoveFileLocal( |
| src_url.path(), dest_url.path(), |
| base::BindOnce(&CreateSnapshotFileOnBlockingPool, profile_path_), |
| base::BindOnce(&DeviceMediaAsyncFileUtil::OnDidMoveFileLocal, |
| weak_ptr_factory_.GetWeakPtr(), |
| std::move(split_callback.first)), |
| base::BindOnce(&OnMoveFileLocalError, std::move(split_callback.second))); |
| } |
| |
| void DeviceMediaAsyncFileUtil::CopyInForeignFile( |
| std::unique_ptr<FileSystemOperationContext> context, |
| const base::FilePath& src_file_path, |
| const FileSystemURL& dest_url, |
| StatusCallback callback) { |
| DCHECK_CURRENTLY_ON(content::BrowserThread::IO); |
| |
| MTPDeviceAsyncDelegate* delegate = |
| MTPDeviceMapService::GetInstance()->GetMTPDeviceAsyncDelegate(dest_url); |
| if (!delegate) { |
| OnCopyInForeignFileError(std::move(callback), |
| base::File::FILE_ERROR_NOT_FOUND); |
| return; |
| } |
| if (delegate->IsReadOnly()) { |
| OnCopyInForeignFileError(std::move(callback), |
| base::File::FILE_ERROR_SECURITY); |
| return; |
| } |
| |
| // Only one of the success or error callbacks will be called here. |
| auto split_callback = base::SplitOnceCallback(std::move(callback)); |
| delegate->CopyFileFromLocal( |
| src_file_path, dest_url.path(), |
| base::BindOnce(&DeviceMediaAsyncFileUtil::OnDidCopyInForeignFile, |
| weak_ptr_factory_.GetWeakPtr(), |
| std::move(split_callback.first)), |
| base::BindOnce(&OnCopyInForeignFileError, |
| std::move(split_callback.second))); |
| } |
| |
| void DeviceMediaAsyncFileUtil::DeleteFile( |
| std::unique_ptr<FileSystemOperationContext> context, |
| const FileSystemURL& url, |
| StatusCallback callback) { |
| DCHECK_CURRENTLY_ON(content::BrowserThread::IO); |
| |
| MTPDeviceAsyncDelegate* const delegate = |
| MTPDeviceMapService::GetInstance()->GetMTPDeviceAsyncDelegate(url); |
| if (!delegate) { |
| OnDeleteFileError(std::move(callback), base::File::FILE_ERROR_NOT_FOUND); |
| return; |
| } |
| if (delegate->IsReadOnly()) { |
| OnDeleteFileError(std::move(callback), base::File::FILE_ERROR_SECURITY); |
| return; |
| } |
| |
| // Only one of the success or error callbacks will be called here. |
| auto split_callback = base::SplitOnceCallback(std::move(callback)); |
| delegate->DeleteFile( |
| url.path(), |
| base::BindOnce(&DeviceMediaAsyncFileUtil::OnDidDeleteFile, |
| weak_ptr_factory_.GetWeakPtr(), |
| std::move(split_callback.first)), |
| base::BindOnce(&OnDeleteFileError, std::move(split_callback.second))); |
| } |
| |
| void DeviceMediaAsyncFileUtil::DeleteDirectory( |
| std::unique_ptr<FileSystemOperationContext> context, |
| const FileSystemURL& url, |
| StatusCallback callback) { |
| DCHECK_CURRENTLY_ON(content::BrowserThread::IO); |
| |
| MTPDeviceAsyncDelegate* const delegate = |
| MTPDeviceMapService::GetInstance()->GetMTPDeviceAsyncDelegate(url); |
| if (!delegate) { |
| OnDeleteDirectoryError(std::move(callback), |
| base::File::FILE_ERROR_NOT_FOUND); |
| return; |
| } |
| if (delegate->IsReadOnly()) { |
| OnDeleteDirectoryError(std::move(callback), |
| base::File::FILE_ERROR_SECURITY); |
| return; |
| } |
| |
| // Only one of the success or error callbacks will be called here. |
| auto split_callback = base::SplitOnceCallback(std::move(callback)); |
| delegate->DeleteDirectory( |
| url.path(), |
| base::BindOnce(&DeviceMediaAsyncFileUtil::OnDidDeleteDirectory, |
| weak_ptr_factory_.GetWeakPtr(), |
| std::move(split_callback.first)), |
| base::BindOnce(&OnDeleteDirectoryError, |
| std::move(split_callback.second))); |
| } |
| |
| void DeviceMediaAsyncFileUtil::DeleteRecursively( |
| std::unique_ptr<FileSystemOperationContext> context, |
| const FileSystemURL& url, |
| StatusCallback callback) { |
| DCHECK_CURRENTLY_ON(content::BrowserThread::IO); |
| std::move(callback).Run(base::File::FILE_ERROR_INVALID_OPERATION); |
| } |
| |
| void DeviceMediaAsyncFileUtil::CreateSnapshotFile( |
| std::unique_ptr<FileSystemOperationContext> context, |
| const FileSystemURL& url, |
| CreateSnapshotFileCallback callback) { |
| DCHECK_CURRENTLY_ON(content::BrowserThread::IO); |
| MTPDeviceAsyncDelegate* delegate = |
| MTPDeviceMapService::GetInstance()->GetMTPDeviceAsyncDelegate(url); |
| if (!delegate) { |
| OnCreateSnapshotFileError(std::move(callback), |
| base::File::FILE_ERROR_NOT_FOUND); |
| return; |
| } |
| |
| scoped_refptr<base::SequencedTaskRunner> task_runner(context->task_runner()); |
| task_runner->PostTaskAndReplyWithResult( |
| FROM_HERE, |
| base::BindOnce(&CreateSnapshotFileOnBlockingPool, profile_path_), |
| base::BindOnce(&OnSnapshotFileCreatedRunTask, std::move(context), |
| std::move(callback), url, validate_media_files())); |
| } |
| |
| std::unique_ptr<storage::FileStreamReader> |
| DeviceMediaAsyncFileUtil::GetFileStreamReader( |
| const FileSystemURL& url, |
| int64_t offset, |
| const base::Time& expected_modification_time, |
| storage::FileSystemContext* context) { |
| MTPDeviceAsyncDelegate* delegate = |
| MTPDeviceMapService::GetInstance()->GetMTPDeviceAsyncDelegate(url); |
| if (!delegate) |
| return nullptr; |
| |
| DCHECK(delegate->IsStreaming()); |
| return std::unique_ptr<storage::FileStreamReader>( |
| new ReadaheadFileStreamReader(new MTPFileStreamReader( |
| context, url, offset, expected_modification_time, |
| validate_media_files()))); |
| } |
| |
| void DeviceMediaAsyncFileUtil::AddWatcher( |
| const storage::FileSystemURL& url, |
| bool recursive, |
| storage::WatcherManager::StatusCallback callback, |
| storage::WatcherManager::NotificationCallback notification_callback) { |
| MTPDeviceAsyncDelegate* const delegate = |
| MTPDeviceMapService::GetInstance()->GetMTPDeviceAsyncDelegate(url); |
| if (!delegate) { |
| std::move(callback).Run(base::File::FILE_ERROR_FAILED); |
| return; |
| } |
| |
| delegate->AddWatcher(url.origin().GetURL(), url.path(), recursive, |
| std::move(callback), std::move(notification_callback)); |
| } |
| |
| void DeviceMediaAsyncFileUtil::RemoveWatcher( |
| const storage::FileSystemURL& url, |
| const bool recursive, |
| storage::WatcherManager::StatusCallback callback) { |
| MTPDeviceAsyncDelegate* const delegate = |
| MTPDeviceMapService::GetInstance()->GetMTPDeviceAsyncDelegate(url); |
| if (!delegate) { |
| std::move(callback).Run(base::File::FILE_ERROR_FAILED); |
| return; |
| } |
| |
| delegate->RemoveWatcher(url.origin().GetURL(), url.path(), recursive, |
| std::move(callback)); |
| } |
| |
| DeviceMediaAsyncFileUtil::DeviceMediaAsyncFileUtil( |
| const base::FilePath& profile_path, |
| MediaFileValidationType validation_type) |
| : profile_path_(profile_path) { |
| if (validation_type == APPLY_MEDIA_FILE_VALIDATION) { |
| media_path_filter_wrapper_ = new MediaPathFilterWrapper; |
| } |
| } |
| |
| void DeviceMediaAsyncFileUtil::OnDidCreateDirectory(StatusCallback callback) { |
| DCHECK_CURRENTLY_ON(content::BrowserThread::IO); |
| |
| std::move(callback).Run(base::File::FILE_OK); |
| } |
| |
| void DeviceMediaAsyncFileUtil::OnDidGetFileInfo( |
| base::SequencedTaskRunner* task_runner, |
| const base::FilePath& path, |
| AsyncFileUtil::GetFileInfoCallback callback, |
| const base::File::Info& file_info) { |
| DCHECK_CURRENTLY_ON(content::BrowserThread::IO); |
| if (file_info.is_directory || !validate_media_files()) { |
| OnDidCheckMediaForGetFileInfo(std::move(callback), file_info, |
| true /* valid */); |
| return; |
| } |
| |
| task_runner->PostTaskAndReplyWithResult( |
| FROM_HERE, |
| base::BindOnce(&MediaPathFilterWrapper::CheckFilePath, |
| media_path_filter_wrapper_, path), |
| base::BindOnce(&OnDidCheckMediaForGetFileInfo, std::move(callback), |
| file_info)); |
| } |
| |
| void DeviceMediaAsyncFileUtil::OnDidReadDirectory( |
| base::SequencedTaskRunner* task_runner, |
| AsyncFileUtil::ReadDirectoryCallback callback, |
| AsyncFileUtil::EntryList file_list, |
| bool has_more) { |
| DCHECK_CURRENTLY_ON(content::BrowserThread::IO); |
| if (!validate_media_files()) { |
| OnDidCheckMediaForReadDirectory(callback, has_more, std::move(file_list)); |
| return; |
| } |
| |
| task_runner->PostTaskAndReplyWithResult( |
| FROM_HERE, |
| base::BindOnce(&MediaPathFilterWrapper::FilterMediaEntries, |
| media_path_filter_wrapper_, std::move(file_list)), |
| base::BindOnce(&OnDidCheckMediaForReadDirectory, callback, has_more)); |
| } |
| |
| void DeviceMediaAsyncFileUtil::OnDidCopyFileLocal(StatusCallback callback) { |
| DCHECK_CURRENTLY_ON(content::BrowserThread::IO); |
| |
| std::move(callback).Run(base::File::FILE_OK); |
| } |
| |
| void DeviceMediaAsyncFileUtil::OnDidMoveFileLocal(StatusCallback callback) { |
| DCHECK_CURRENTLY_ON(content::BrowserThread::IO); |
| |
| std::move(callback).Run(base::File::FILE_OK); |
| } |
| |
| void DeviceMediaAsyncFileUtil::OnDidCopyInForeignFile(StatusCallback callback) { |
| DCHECK_CURRENTLY_ON(content::BrowserThread::IO); |
| |
| std::move(callback).Run(base::File::FILE_OK); |
| } |
| |
| void DeviceMediaAsyncFileUtil::OnDidDeleteFile(StatusCallback callback) { |
| DCHECK_CURRENTLY_ON(content::BrowserThread::IO); |
| |
| std::move(callback).Run(base::File::FILE_OK); |
| } |
| |
| void DeviceMediaAsyncFileUtil::OnDidDeleteDirectory(StatusCallback callback) { |
| DCHECK_CURRENTLY_ON(content::BrowserThread::IO); |
| |
| std::move(callback).Run(base::File::FILE_OK); |
| } |
| |
| bool DeviceMediaAsyncFileUtil::validate_media_files() const { |
| return media_path_filter_wrapper_.get() != nullptr; |
| } |