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.
5 #include "chrome/browser/media_galleries/fileapi/mtp_file_stream_reader.h"
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"
19 using storage::FileStreamReader
;
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(
31 void CallCompletionCallbackWithPlatformFileError(
32 const net::CompletionCallback
& callback
,
33 base::File::Error file_error
) {
34 callback
.Run(net::FileErrorToNetError(file_error
));
37 void CallInt64CompletionCallbackWithPlatformFileError(
38 const net::Int64CompletionCallback
& callback
,
39 base::File::Error file_error
) {
40 callback
.Run(net::FileErrorToNetError(file_error
));
44 const storage::FileSystemURL
& url
,
45 const scoped_refptr
<net::IOBuffer
>& buf
,
48 const MTPDeviceAsyncDelegate::ReadBytesSuccessCallback
& success_callback
,
49 const net::CompletionCallback
& error_callback
) {
50 MTPDeviceAsyncDelegate
* delegate
= GetMTPDeviceDelegate(url
);
52 error_callback
.Run(net::ERR_FAILED
);
62 base::Bind(&CallCompletionCallbackWithPlatformFileError
, error_callback
));
67 MTPFileStreamReader::MTPFileStreamReader(
68 storage::FileSystemContext
* file_system_context
,
69 const storage::FileSystemURL
& url
,
71 const base::Time
& expected_modification_time
,
72 bool do_media_header_validation
)
73 : file_system_context_(file_system_context
),
75 current_offset_(initial_offset
),
76 expected_modification_time_(expected_modification_time
),
77 media_header_validated_(!do_media_header_validation
),
81 MTPFileStreamReader::~MTPFileStreamReader() {
84 int MTPFileStreamReader::Read(net::IOBuffer
* buf
, int buf_len
,
85 const net::CompletionCallback
& callback
) {
86 DCHECK_CURRENTLY_ON(content::BrowserThread::IO
);
88 MTPDeviceAsyncDelegate
* delegate
= GetMTPDeviceDelegate(url_
);
90 return net::ERR_FAILED
;
92 if (!media_header_validated_
) {
93 scoped_refptr
<net::IOBuffer
> header_buf
;
94 int header_buf_len
= 0;
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.
100 header_buf_len
= buf_len
;
102 // Otherwise, make a special request for the header.
103 header_buf
= new net::IOBuffer(net::kMaxBytesToSniff
);
104 header_buf_len
= net::kMaxBytesToSniff
;
111 base::Bind(&MTPFileStreamReader::FinishValidateMediaHeader
,
112 weak_factory_
.GetWeakPtr(),
114 make_scoped_refptr(buf
),
118 return net::ERR_IO_PENDING
;
121 ReadBytes(url_
, buf
, current_offset_
, buf_len
,
122 base::Bind(&MTPFileStreamReader::FinishRead
,
123 weak_factory_
.GetWeakPtr(), callback
),
126 return net::ERR_IO_PENDING
;
129 int64
MTPFileStreamReader::GetLength(
130 const net::Int64CompletionCallback
& callback
) {
131 DCHECK_CURRENTLY_ON(content::BrowserThread::IO
);
133 MTPDeviceAsyncDelegate
* delegate
= GetMTPDeviceDelegate(url_
);
135 return net::ERR_FAILED
;
137 delegate
->GetFileInfo(
139 base::Bind(&MTPFileStreamReader::FinishGetLength
,
140 weak_factory_
.GetWeakPtr(), callback
),
141 base::Bind(&CallInt64CompletionCallbackWithPlatformFileError
,
144 return net::ERR_IO_PENDING
;
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
);
162 media_header_validated_
= true;
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
);
169 // Header buffer isn't the same as the original read buffer. Make a separate
171 ReadBytes(url_
, buf
, current_offset_
, buf_len
,
172 base::Bind(&MTPFileStreamReader::FinishRead
,
173 weak_factory_
.GetWeakPtr(), callback
),
177 void MTPFileStreamReader::FinishRead(const net::CompletionCallback
& callback
,
178 const base::File::Info
& file_info
,
180 DCHECK_CURRENTLY_ON(content::BrowserThread::IO
);
182 if (!VerifySnapshotTime(expected_modification_time_
, file_info
)) {
183 callback
.Run(net::ERR_UPLOAD_FILE_CHANGED
);
187 DCHECK_GE(bytes_read
, 0);
188 current_offset_
+= bytes_read
;
189 callback
.Run(bytes_read
);
192 void MTPFileStreamReader::FinishGetLength(
193 const net::Int64CompletionCallback
& callback
,
194 const base::File::Info
& file_info
) {
195 DCHECK_CURRENTLY_ON(content::BrowserThread::IO
);
197 if (!VerifySnapshotTime(expected_modification_time_
, file_info
)) {
198 callback
.Run(net::ERR_UPLOAD_FILE_CHANGED
);
202 callback
.Run(file_info
.size
);