1 // Copyright 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/linux/mtp_device_task_helper.h" 6 7 #include <algorithm> 8 9 #include "base/logging.h" 10 #include "base/numerics/safe_conversions.h" 11 #include "base/strings/string_number_conversions.h" 12 #include "chrome/browser/media_galleries/linux/mtp_device_object_enumerator.h" 13 #include "chrome/browser/media_galleries/linux/mtp_read_file_worker.h" 14 #include "chrome/browser/media_galleries/linux/snapshot_file_details.h" 15 #include "components/storage_monitor/storage_monitor.h" 16 #include "content/public/browser/browser_thread.h" 17 #include "device/media_transfer_protocol/media_transfer_protocol_manager.h" 18 #include "net/base/io_buffer.h" 19 #include "storage/browser/fileapi/async_file_util.h" 20 #include "storage/common/fileapi/file_system_util.h" 21 #include "third_party/cros_system_api/dbus/service_constants.h" 22 23 using storage_monitor::StorageMonitor; 24 25 namespace { 26 27 // Does nothing. 28 // This method is used to handle the results of 29 // MediaTransferProtocolManager::CloseStorage method call. 30 void DoNothing(bool error) { 31 } 32 33 device::MediaTransferProtocolManager* GetMediaTransferProtocolManager() { 34 return StorageMonitor::GetInstance()->media_transfer_protocol_manager(); 35 } 36 37 base::File::Info FileInfoFromMTPFileEntry(const MtpFileEntry& file_entry) { 38 base::File::Info file_entry_info; 39 file_entry_info.size = file_entry.file_size(); 40 file_entry_info.is_directory = 41 file_entry.file_type() == MtpFileEntry::FILE_TYPE_FOLDER; 42 file_entry_info.is_symbolic_link = false; 43 file_entry_info.last_modified = 44 base::Time::FromTimeT(file_entry.modification_time()); 45 file_entry_info.last_accessed = file_entry_info.last_modified; 46 file_entry_info.creation_time = base::Time(); 47 return file_entry_info; 48 } 49 50 } // namespace 51 52 MTPDeviceTaskHelper::MTPDeviceTaskHelper() 53 : weak_ptr_factory_(this) { 54 DCHECK_CURRENTLY_ON(content::BrowserThread::UI); 55 } 56 57 MTPDeviceTaskHelper::~MTPDeviceTaskHelper() { 58 DCHECK_CURRENTLY_ON(content::BrowserThread::UI); 59 } 60 61 void MTPDeviceTaskHelper::OpenStorage(const std::string& storage_name, 62 const OpenStorageCallback& callback) { 63 DCHECK_CURRENTLY_ON(content::BrowserThread::UI); 64 DCHECK(!storage_name.empty()); 65 if (!device_handle_.empty()) { 66 content::BrowserThread::PostTask(content::BrowserThread::IO, 67 FROM_HERE, 68 base::Bind(callback, true)); 69 return; 70 } 71 GetMediaTransferProtocolManager()->OpenStorage( 72 storage_name, mtpd::kReadOnlyMode, 73 base::Bind(&MTPDeviceTaskHelper::OnDidOpenStorage, 74 weak_ptr_factory_.GetWeakPtr(), 75 callback)); 76 } 77 78 void MTPDeviceTaskHelper::GetFileInfo( 79 uint32 file_id, 80 const GetFileInfoSuccessCallback& success_callback, 81 const ErrorCallback& error_callback) { 82 DCHECK_CURRENTLY_ON(content::BrowserThread::UI); 83 if (device_handle_.empty()) 84 return HandleDeviceError(error_callback, base::File::FILE_ERROR_FAILED); 85 86 GetMediaTransferProtocolManager()->GetFileInfo( 87 device_handle_, file_id, 88 base::Bind(&MTPDeviceTaskHelper::OnGetFileInfo, 89 weak_ptr_factory_.GetWeakPtr(), 90 success_callback, 91 error_callback)); 92 } 93 94 void MTPDeviceTaskHelper::ReadDirectory( 95 uint32 dir_id, 96 const ReadDirectorySuccessCallback& success_callback, 97 const ErrorCallback& error_callback) { 98 DCHECK_CURRENTLY_ON(content::BrowserThread::UI); 99 if (device_handle_.empty()) 100 return HandleDeviceError(error_callback, base::File::FILE_ERROR_FAILED); 101 102 GetMediaTransferProtocolManager()->ReadDirectory( 103 device_handle_, dir_id, 104 base::Bind(&MTPDeviceTaskHelper::OnDidReadDirectory, 105 weak_ptr_factory_.GetWeakPtr(), 106 success_callback, 107 error_callback)); 108 } 109 110 void MTPDeviceTaskHelper::WriteDataIntoSnapshotFile( 111 const SnapshotRequestInfo& request_info, 112 const base::File::Info& snapshot_file_info) { 113 DCHECK_CURRENTLY_ON(content::BrowserThread::UI); 114 if (device_handle_.empty()) { 115 return HandleDeviceError(request_info.error_callback, 116 base::File::FILE_ERROR_FAILED); 117 } 118 119 if (!read_file_worker_) 120 read_file_worker_.reset(new MTPReadFileWorker(device_handle_)); 121 read_file_worker_->WriteDataIntoSnapshotFile(request_info, 122 snapshot_file_info); 123 } 124 125 void MTPDeviceTaskHelper::ReadBytes( 126 const MTPDeviceAsyncDelegate::ReadBytesRequest& request) { 127 DCHECK_CURRENTLY_ON(content::BrowserThread::UI); 128 if (device_handle_.empty()) { 129 return HandleDeviceError(request.error_callback, 130 base::File::FILE_ERROR_FAILED); 131 } 132 133 GetMediaTransferProtocolManager()->GetFileInfo( 134 device_handle_, request.file_id, 135 base::Bind(&MTPDeviceTaskHelper::OnGetFileInfoToReadBytes, 136 weak_ptr_factory_.GetWeakPtr(), request)); 137 } 138 139 void MTPDeviceTaskHelper::CloseStorage() const { 140 DCHECK_CURRENTLY_ON(content::BrowserThread::UI); 141 if (device_handle_.empty()) 142 return; 143 GetMediaTransferProtocolManager()->CloseStorage(device_handle_, 144 base::Bind(&DoNothing)); 145 } 146 147 void MTPDeviceTaskHelper::OnDidOpenStorage( 148 const OpenStorageCallback& completion_callback, 149 const std::string& device_handle, 150 bool error) { 151 DCHECK_CURRENTLY_ON(content::BrowserThread::UI); 152 device_handle_ = device_handle; 153 content::BrowserThread::PostTask(content::BrowserThread::IO, 154 FROM_HERE, 155 base::Bind(completion_callback, !error)); 156 } 157 158 void MTPDeviceTaskHelper::OnGetFileInfo( 159 const GetFileInfoSuccessCallback& success_callback, 160 const ErrorCallback& error_callback, 161 const MtpFileEntry& file_entry, 162 bool error) const { 163 DCHECK_CURRENTLY_ON(content::BrowserThread::UI); 164 if (error) { 165 return HandleDeviceError(error_callback, 166 base::File::FILE_ERROR_NOT_FOUND); 167 } 168 169 content::BrowserThread::PostTask( 170 content::BrowserThread::IO, 171 FROM_HERE, 172 base::Bind(success_callback, FileInfoFromMTPFileEntry(file_entry))); 173 } 174 175 void MTPDeviceTaskHelper::OnDidReadDirectory( 176 const ReadDirectorySuccessCallback& success_callback, 177 const ErrorCallback& error_callback, 178 const std::vector<MtpFileEntry>& file_entries, 179 bool has_more, 180 bool error) const { 181 DCHECK_CURRENTLY_ON(content::BrowserThread::UI); 182 if (error) 183 return HandleDeviceError(error_callback, base::File::FILE_ERROR_FAILED); 184 185 storage::AsyncFileUtil::EntryList entries; 186 base::FilePath current; 187 MTPDeviceObjectEnumerator file_enum(file_entries); 188 while (!(current = file_enum.Next()).empty()) { 189 storage::DirectoryEntry entry; 190 entry.name = storage::VirtualPath::BaseName(current).value(); 191 uint32 file_id = 0; 192 bool ret = file_enum.GetEntryId(&file_id); 193 DCHECK(ret); 194 entry.name.push_back(','); 195 entry.name += base::UintToString(file_id); 196 entry.is_directory = file_enum.IsDirectory(); 197 entry.size = file_enum.Size(); 198 entry.last_modified_time = file_enum.LastModifiedTime(); 199 entries.push_back(entry); 200 } 201 content::BrowserThread::PostTask( 202 content::BrowserThread::IO, 203 FROM_HERE, 204 base::Bind(success_callback, entries, has_more)); 205 } 206 207 void MTPDeviceTaskHelper::OnGetFileInfoToReadBytes( 208 const MTPDeviceAsyncDelegate::ReadBytesRequest& request, 209 const MtpFileEntry& file_entry, 210 bool error) { 211 DCHECK_CURRENTLY_ON(content::BrowserThread::UI); 212 DCHECK(request.buf.get()); 213 DCHECK_GE(request.buf_len, 0); 214 DCHECK_GE(request.offset, 0); 215 if (error) { 216 return HandleDeviceError(request.error_callback, 217 base::File::FILE_ERROR_FAILED); 218 } 219 220 base::File::Info file_info = FileInfoFromMTPFileEntry(file_entry); 221 if (file_info.is_directory) { 222 return HandleDeviceError(request.error_callback, 223 base::File::FILE_ERROR_NOT_A_FILE); 224 } else if (file_info.size < 0 || file_info.size > kuint32max || 225 request.offset > file_info.size) { 226 return HandleDeviceError(request.error_callback, 227 base::File::FILE_ERROR_FAILED); 228 } else if (request.offset == file_info.size) { 229 content::BrowserThread::PostTask(content::BrowserThread::IO, 230 FROM_HERE, 231 base::Bind(request.success_callback, 232 file_info, 0u)); 233 return; 234 } 235 236 uint32 bytes_to_read = std::min( 237 base::checked_cast<uint32>(request.buf_len), 238 base::saturated_cast<uint32>(file_info.size - request.offset)); 239 240 GetMediaTransferProtocolManager()->ReadFileChunk( 241 device_handle_, 242 request.file_id, 243 base::checked_cast<uint32>(request.offset), 244 bytes_to_read, 245 base::Bind(&MTPDeviceTaskHelper::OnDidReadBytes, 246 weak_ptr_factory_.GetWeakPtr(), request, file_info)); 247 } 248 249 void MTPDeviceTaskHelper::OnDidReadBytes( 250 const MTPDeviceAsyncDelegate::ReadBytesRequest& request, 251 const base::File::Info& file_info, 252 const std::string& data, 253 bool error) const { 254 DCHECK_CURRENTLY_ON(content::BrowserThread::UI); 255 if (error) { 256 return HandleDeviceError(request.error_callback, 257 base::File::FILE_ERROR_FAILED); 258 } 259 260 CHECK_LE(base::checked_cast<int>(data.length()), request.buf_len); 261 std::copy(data.begin(), data.end(), request.buf->data()); 262 263 content::BrowserThread::PostTask(content::BrowserThread::IO, 264 FROM_HERE, 265 base::Bind(request.success_callback, 266 file_info, data.length())); 267 } 268 269 void MTPDeviceTaskHelper::HandleDeviceError( 270 const ErrorCallback& error_callback, 271 base::File::Error error) const { 272 DCHECK_CURRENTLY_ON(content::BrowserThread::UI); 273 content::BrowserThread::PostTask(content::BrowserThread::IO, 274 FROM_HERE, 275 base::Bind(error_callback, error)); 276 } 277