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 "base/platform_file.h"
11 #include "chrome/browser/media_galleries/fileapi/mtp_device_async_delegate.h"
12 #include "chrome/browser/media_galleries/fileapi/mtp_device_map_service.h"
13 #include "chrome/browser/media_galleries/fileapi/native_media_file_util.h"
14 #include "content/public/browser/browser_thread.h"
15 #include "net/base/io_buffer.h"
16 #include "net/base/mime_sniffer.h"
17 #include "net/base/net_errors.h"
18 #include "webkit/browser/fileapi/file_system_context.h"
20 using webkit_blob::FileStreamReader;
24 // Called on the IO thread.
25 MTPDeviceAsyncDelegate* GetMTPDeviceDelegate(
26 const fileapi::FileSystemURL& url) {
27 DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
28 return MTPDeviceMapService::GetInstance()->GetMTPDeviceAsyncDelegate(
32 void CallCompletionCallbackWithPlatformFileError(
33 const net::CompletionCallback& callback,
34 base::File::Error file_error) {
35 callback.Run(net::FileErrorToNetError(file_error));
38 void CallInt64CompletionCallbackWithPlatformFileError(
39 const net::Int64CompletionCallback& callback,
40 base::File::Error file_error) {
41 callback.Run(net::FileErrorToNetError(file_error));
45 const fileapi::FileSystemURL& url, net::IOBuffer* buf, int64 offset,
47 const MTPDeviceAsyncDelegate::ReadBytesSuccessCallback& success_callback,
48 const net::CompletionCallback& error_callback) {
49 MTPDeviceAsyncDelegate* delegate = GetMTPDeviceDelegate(url);
51 error_callback.Run(net::ERR_FAILED);
57 make_scoped_refptr(buf),
61 base::Bind(&CallCompletionCallbackWithPlatformFileError, error_callback));
66 MTPFileStreamReader::MTPFileStreamReader(
67 fileapi::FileSystemContext* file_system_context,
68 const fileapi::FileSystemURL& url,
70 const base::Time& expected_modification_time,
71 bool do_media_header_validation)
72 : file_system_context_(file_system_context),
74 current_offset_(initial_offset),
75 expected_modification_time_(expected_modification_time),
76 media_header_validated_(!do_media_header_validation),
80 MTPFileStreamReader::~MTPFileStreamReader() {
83 int MTPFileStreamReader::Read(net::IOBuffer* buf, int buf_len,
84 const net::CompletionCallback& callback) {
85 DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
87 MTPDeviceAsyncDelegate* delegate = GetMTPDeviceDelegate(url_);
89 return net::ERR_FAILED;
91 if (!media_header_validated_) {
92 scoped_refptr<net::IOBuffer> header_buf;
93 int header_buf_len = 0;
95 if (current_offset_ == 0 && buf_len >= net::kMaxBytesToSniff) {
96 // If requested read includes all the header bytes, we read directly to
97 // the original buffer, and validate the header bytes within that.
99 header_buf_len = buf_len;
101 // Otherwise, make a special request for the header.
102 header_buf = new net::IOBuffer(net::kMaxBytesToSniff);
103 header_buf_len = net::kMaxBytesToSniff;
106 ReadBytes(url_, header_buf, 0, header_buf_len,
107 base::Bind(&MTPFileStreamReader::FinishValidateMediaHeader,
108 weak_factory_.GetWeakPtr(), header_buf,
109 make_scoped_refptr(buf), buf_len, callback),
111 return net::ERR_IO_PENDING;
114 ReadBytes(url_, buf, current_offset_, buf_len,
115 base::Bind(&MTPFileStreamReader::FinishRead,
116 weak_factory_.GetWeakPtr(), callback),
119 return net::ERR_IO_PENDING;
122 int64 MTPFileStreamReader::GetLength(
123 const net::Int64CompletionCallback& callback) {
124 DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
126 MTPDeviceAsyncDelegate* delegate = GetMTPDeviceDelegate(url_);
128 return net::ERR_FAILED;
130 delegate->GetFileInfo(
132 base::Bind(&MTPFileStreamReader::FinishGetLength,
133 weak_factory_.GetWeakPtr(), callback),
134 base::Bind(&CallInt64CompletionCallbackWithPlatformFileError,
137 return net::ERR_IO_PENDING;
140 void MTPFileStreamReader::FinishValidateMediaHeader(
141 net::IOBuffer* header_buf,
142 net::IOBuffer* buf, int buf_len,
143 const net::CompletionCallback& callback,
144 const base::File::Info& file_info,
145 int header_bytes_read) {
146 DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
147 DCHECK_GE(header_bytes_read, 0);
148 base::File::Error error = NativeMediaFileUtil::BufferIsMediaHeader(
149 header_buf, header_bytes_read);
150 if (error != base::File::FILE_OK) {
151 CallCompletionCallbackWithPlatformFileError(callback, error);
155 media_header_validated_ = true;
157 // Finish the read immediately if we've already finished reading into the
158 // originally requested buffer.
159 if (header_buf == buf)
160 return FinishRead(callback, file_info, header_bytes_read);
162 // Header buffer isn't the same as the original read buffer. Make a separate
164 ReadBytes(url_, buf, current_offset_, buf_len,
165 base::Bind(&MTPFileStreamReader::FinishRead,
166 weak_factory_.GetWeakPtr(), callback),
170 void MTPFileStreamReader::FinishRead(const net::CompletionCallback& callback,
171 const base::File::Info& file_info,
173 DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
175 if (!VerifySnapshotTime(expected_modification_time_, file_info)) {
176 callback.Run(net::ERR_UPLOAD_FILE_CHANGED);
180 DCHECK_GE(bytes_read, 0);
181 current_offset_ += bytes_read;
182 callback.Run(bytes_read);
185 void MTPFileStreamReader::FinishGetLength(
186 const net::Int64CompletionCallback& callback,
187 const base::File::Info& file_info) {
188 DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
190 if (!VerifySnapshotTime(expected_modification_time_, file_info)) {
191 callback.Run(net::ERR_UPLOAD_FILE_CHANGED);
195 callback.Run(file_info.size);