blob: feb8ea880f0a3e755cbac3e67c3f5c81f603405d [file] [log] [blame]
// Copyright 2014 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/mtp_file_stream_reader.h"
#include <algorithm>
#include <utility>
#include "base/numerics/safe_conversions.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/native_media_file_util.h"
#include "content/public/browser/browser_thread.h"
#include "net/base/io_buffer.h"
#include "net/base/mime_sniffer.h"
#include "net/base/net_errors.h"
#include "storage/browser/file_system/file_system_context.h"
using storage::FileStreamReader;
namespace {
} // namespace
MTPFileStreamReader::MTPFileStreamReader(
storage::FileSystemContext* file_system_context,
const storage::FileSystemURL& url,
int64_t initial_offset,
const base::Time& expected_modification_time,
bool do_media_header_validation)
: file_system_context_(file_system_context),
url_(url),
current_offset_(initial_offset),
expected_modification_time_(expected_modification_time),
media_header_validated_(!do_media_header_validation) {}
MTPFileStreamReader::~MTPFileStreamReader() {
}
int MTPFileStreamReader::Read(net::IOBuffer* buf,
int buf_len,
net::CompletionOnceCallback callback) {
DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
MTPDeviceAsyncDelegate* delegate =
MTPDeviceMapService::GetInstance()->GetMTPDeviceAsyncDelegate(url_);
if (!delegate)
return net::ERR_FAILED;
read_callback_ = std::move(callback);
if (!media_header_validated_) {
scoped_refptr<net::IOBuffer> header_buf;
int header_buf_len = 0;
if (current_offset_ == 0 && buf_len >= net::kMaxBytesToSniff) {
// If requested read includes all the header bytes, we read directly to
// the original buffer, and validate the header bytes within that.
header_buf = buf;
header_buf_len = buf_len;
} else {
// Otherwise, make a special request for the header.
header_buf =
base::MakeRefCounted<net::IOBufferWithSize>(net::kMaxBytesToSniff);
header_buf_len = net::kMaxBytesToSniff;
}
ReadBytes(url_, header_buf.get(), 0, header_buf_len,
base::BindOnce(&MTPFileStreamReader::FinishValidateMediaHeader,
weak_factory_.GetWeakPtr(),
base::RetainedRef(header_buf),
base::RetainedRef(buf), buf_len));
return net::ERR_IO_PENDING;
}
ReadBytes(url_, buf, current_offset_, buf_len,
base::BindOnce(&MTPFileStreamReader::FinishRead,
weak_factory_.GetWeakPtr()));
return net::ERR_IO_PENDING;
}
int64_t MTPFileStreamReader::GetLength(
net::Int64CompletionOnceCallback callback) {
DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
MTPDeviceAsyncDelegate* delegate =
MTPDeviceMapService::GetInstance()->GetMTPDeviceAsyncDelegate(url_);
if (!delegate)
return net::ERR_FAILED;
get_length_callback_ = std::move(callback);
delegate->GetFileInfo(
url_.path(),
base::BindOnce(&MTPFileStreamReader::FinishGetLength,
weak_factory_.GetWeakPtr()),
base::BindRepeating(
&MTPFileStreamReader::CallGetLengthCallbackWithPlatformFileError,
weak_factory_.GetWeakPtr()));
return net::ERR_IO_PENDING;
}
void MTPFileStreamReader::FinishValidateMediaHeader(
net::IOBuffer* header_buf,
net::IOBuffer* buf, int buf_len,
const base::File::Info& file_info,
int header_bytes_read) {
DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
DCHECK_GE(header_bytes_read, 0);
base::File::Error error = NativeMediaFileUtil::BufferIsMediaHeader(
header_buf, header_bytes_read);
if (error != base::File::FILE_OK) {
CallReadCallbackwithPlatformFileError(error);
return;
}
media_header_validated_ = true;
// Finish the read immediately if we've already finished reading into the
// originally requested buffer.
if (header_buf == buf)
return FinishRead(file_info, header_bytes_read);
// Header buffer isn't the same as the original read buffer. Make a separate
// request for that.
ReadBytes(url_, buf, current_offset_, buf_len,
base::BindOnce(&MTPFileStreamReader::FinishRead,
weak_factory_.GetWeakPtr()));
}
void MTPFileStreamReader::FinishRead(const base::File::Info& file_info,
int bytes_read) {
DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
if (!VerifySnapshotTime(expected_modification_time_, file_info)) {
std::move(read_callback_).Run(net::ERR_UPLOAD_FILE_CHANGED);
return;
}
DCHECK_GE(bytes_read, 0);
current_offset_ += bytes_read;
std::move(read_callback_).Run(bytes_read);
}
void MTPFileStreamReader::FinishGetLength(
const base::File::Info& file_info) {
DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
if (!VerifySnapshotTime(expected_modification_time_, file_info)) {
std::move(get_length_callback_).Run(net::ERR_UPLOAD_FILE_CHANGED);
return;
}
std::move(get_length_callback_).Run(file_info.size);
}
void MTPFileStreamReader::CallReadCallbackwithPlatformFileError(
base::File::Error file_error) {
std::move(read_callback_).Run(net::FileErrorToNetError(file_error));
}
void MTPFileStreamReader::CallGetLengthCallbackWithPlatformFileError(
base::File::Error file_error) {
std::move(get_length_callback_).Run(net::FileErrorToNetError(file_error));
}
void MTPFileStreamReader::ReadBytes(
const storage::FileSystemURL& url,
const scoped_refptr<net::IOBuffer>& buf,
int64_t offset,
int buf_len,
MTPDeviceAsyncDelegate::ReadBytesSuccessCallback success_callback) {
MTPDeviceAsyncDelegate* delegate =
MTPDeviceMapService::GetInstance()->GetMTPDeviceAsyncDelegate(url);
if (!delegate) {
std::move(read_callback_).Run(net::ERR_FAILED);
return;
}
delegate->ReadBytes(
url.path(), buf, offset, buf_len, std::move(success_callback),
base::BindRepeating(
&MTPFileStreamReader::CallReadCallbackwithPlatformFileError,
weak_factory_.GetWeakPtr()));
}