1 // Copyright 2014 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/mtp_file_stream_reader.h" 6 7 #include <algorithm> 8 9 #include "base/numerics/safe_conversions.h" 10 #include "chrome/browser/media_galleries/fileapi/mtp_device_async_delegate.h" 11 #include "chrome/browser/media_galleries/fileapi/mtp_device_map_service.h" 12 #include "chrome/browser/media_galleries/fileapi/native_media_file_util.h" 13 #include "content/public/browser/browser_thread.h" 14 #include "net/base/io_buffer.h" 15 #include "net/base/mime_sniffer.h" 16 #include "net/base/net_errors.h" 17 #include "storage/browser/fileapi/file_system_context.h" 18 19 using storage::FileStreamReader; 20 21 namespace { 22 23 // Called on the IO thread. 24 MTPDeviceAsyncDelegate* GetMTPDeviceDelegate( 25 const storage::FileSystemURL& url) { 26 DCHECK_CURRENTLY_ON(content::BrowserThread::IO); 27 return MTPDeviceMapService::GetInstance()->GetMTPDeviceAsyncDelegate( 28 url.filesystem_id()); 29 } 30 31 void CallCompletionCallbackWithPlatformFileError( 32 const net::CompletionCallback& callback, 33 base::File::Error file_error) { 34 callback.Run(net::FileErrorToNetError(file_error)); 35 } 36 37 void CallInt64CompletionCallbackWithPlatformFileError( 38 const net::Int64CompletionCallback& callback, 39 base::File::Error file_error) { 40 callback.Run(net::FileErrorToNetError(file_error)); 41 } 42 43 void ReadBytes( 44 const storage::FileSystemURL& url, 45 const scoped_refptr<net::IOBuffer>& buf, 46 int64 offset, 47 int buf_len, 48 const MTPDeviceAsyncDelegate::ReadBytesSuccessCallback& success_callback, 49 const net::CompletionCallback& error_callback) { 50 MTPDeviceAsyncDelegate* delegate = GetMTPDeviceDelegate(url); 51 if (!delegate) { 52 error_callback.Run(net::ERR_FAILED); 53 return; 54 } 55 56 delegate->ReadBytes( 57 url.path(), 58 buf, 59 offset, 60 buf_len, 61 success_callback, 62 base::Bind(&CallCompletionCallbackWithPlatformFileError, error_callback)); 63 } 64 65 } // namespace 66 67 MTPFileStreamReader::MTPFileStreamReader( 68 storage::FileSystemContext* file_system_context, 69 const storage::FileSystemURL& url, 70 int64 initial_offset, 71 const base::Time& expected_modification_time, 72 bool do_media_header_validation) 73 : file_system_context_(file_system_context), 74 url_(url), 75 current_offset_(initial_offset), 76 expected_modification_time_(expected_modification_time), 77 media_header_validated_(!do_media_header_validation), 78 weak_factory_(this) { 79 } 80 81 MTPFileStreamReader::~MTPFileStreamReader() { 82 } 83 84 int MTPFileStreamReader::Read(net::IOBuffer* buf, int buf_len, 85 const net::CompletionCallback& callback) { 86 DCHECK_CURRENTLY_ON(content::BrowserThread::IO); 87 88 MTPDeviceAsyncDelegate* delegate = GetMTPDeviceDelegate(url_); 89 if (!delegate) 90 return net::ERR_FAILED; 91 92 if (!media_header_validated_) { 93 scoped_refptr<net::IOBuffer> header_buf; 94 int header_buf_len = 0; 95 96 if (current_offset_ == 0 && buf_len >= net::kMaxBytesToSniff) { 97 // If requested read includes all the header bytes, we read directly to 98 // the original buffer, and validate the header bytes within that. 99 header_buf = buf; 100 header_buf_len = buf_len; 101 } else { 102 // Otherwise, make a special request for the header. 103 header_buf = new net::IOBuffer(net::kMaxBytesToSniff); 104 header_buf_len = net::kMaxBytesToSniff; 105 } 106 107 ReadBytes(url_, 108 header_buf.get(), 109 0, 110 header_buf_len, 111 base::Bind(&MTPFileStreamReader::FinishValidateMediaHeader, 112 weak_factory_.GetWeakPtr(), 113 header_buf, 114 make_scoped_refptr(buf), 115 buf_len, 116 callback), 117 callback); 118 return net::ERR_IO_PENDING; 119 } 120 121 ReadBytes(url_, buf, current_offset_, buf_len, 122 base::Bind(&MTPFileStreamReader::FinishRead, 123 weak_factory_.GetWeakPtr(), callback), 124 callback); 125 126 return net::ERR_IO_PENDING; 127 } 128 129 int64 MTPFileStreamReader::GetLength( 130 const net::Int64CompletionCallback& callback) { 131 DCHECK_CURRENTLY_ON(content::BrowserThread::IO); 132 133 MTPDeviceAsyncDelegate* delegate = GetMTPDeviceDelegate(url_); 134 if (!delegate) 135 return net::ERR_FAILED; 136 137 delegate->GetFileInfo( 138 url_.path(), 139 base::Bind(&MTPFileStreamReader::FinishGetLength, 140 weak_factory_.GetWeakPtr(), callback), 141 base::Bind(&CallInt64CompletionCallbackWithPlatformFileError, 142 callback)); 143 144 return net::ERR_IO_PENDING; 145 } 146 147 void MTPFileStreamReader::FinishValidateMediaHeader( 148 net::IOBuffer* header_buf, 149 net::IOBuffer* buf, int buf_len, 150 const net::CompletionCallback& callback, 151 const base::File::Info& file_info, 152 int header_bytes_read) { 153 DCHECK_CURRENTLY_ON(content::BrowserThread::IO); 154 DCHECK_GE(header_bytes_read, 0); 155 base::File::Error error = NativeMediaFileUtil::BufferIsMediaHeader( 156 header_buf, header_bytes_read); 157 if (error != base::File::FILE_OK) { 158 CallCompletionCallbackWithPlatformFileError(callback, error); 159 return; 160 } 161 162 media_header_validated_ = true; 163 164 // Finish the read immediately if we've already finished reading into the 165 // originally requested buffer. 166 if (header_buf == buf) 167 return FinishRead(callback, file_info, header_bytes_read); 168 169 // Header buffer isn't the same as the original read buffer. Make a separate 170 // request for that. 171 ReadBytes(url_, buf, current_offset_, buf_len, 172 base::Bind(&MTPFileStreamReader::FinishRead, 173 weak_factory_.GetWeakPtr(), callback), 174 callback); 175 } 176 177 void MTPFileStreamReader::FinishRead(const net::CompletionCallback& callback, 178 const base::File::Info& file_info, 179 int bytes_read) { 180 DCHECK_CURRENTLY_ON(content::BrowserThread::IO); 181 182 if (!VerifySnapshotTime(expected_modification_time_, file_info)) { 183 callback.Run(net::ERR_UPLOAD_FILE_CHANGED); 184 return; 185 } 186 187 DCHECK_GE(bytes_read, 0); 188 current_offset_ += bytes_read; 189 callback.Run(bytes_read); 190 } 191 192 void MTPFileStreamReader::FinishGetLength( 193 const net::Int64CompletionCallback& callback, 194 const base::File::Info& file_info) { 195 DCHECK_CURRENTLY_ON(content::BrowserThread::IO); 196 197 if (!VerifySnapshotTime(expected_modification_time_, file_info)) { 198 callback.Run(net::ERR_UPLOAD_FILE_CHANGED); 199 return; 200 } 201 202 callback.Run(file_info.size); 203 } 204