| // Copyright (c) 2012 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "storage/browser/fileapi/local_file_stream_reader.h" |
| |
| #include <stdint.h> |
| |
| #include <utility> |
| |
| #include "base/bind.h" |
| #include "base/files/file_util.h" |
| #include "base/location.h" |
| #include "base/logging.h" |
| #include "base/memory/ptr_util.h" |
| #include "base/task_runner.h" |
| #include "base/task_runner_util.h" |
| #include "net/base/file_stream.h" |
| #include "net/base/io_buffer.h" |
| #include "net/base/net_errors.h" |
| |
| namespace storage { |
| |
| namespace { |
| |
| const int kOpenFlagsForRead = base::File::FLAG_OPEN | |
| base::File::FLAG_READ | |
| base::File::FLAG_ASYNC; |
| |
| struct GetFileInfoResults { |
| base::File::Error error; |
| base::File::Info info; |
| }; |
| |
| using GetFileInfoCallback = |
| base::OnceCallback<void(base::File::Error, const base::File::Info&)>; |
| |
| GetFileInfoResults DoGetFileInfo(const base::FilePath& path) { |
| GetFileInfoResults results; |
| if (!base::PathExists(path)) { |
| results.error = base::File::FILE_ERROR_NOT_FOUND; |
| return results; |
| } |
| results.error = base::GetFileInfo(path, &results.info) |
| ? base::File::FILE_OK |
| : base::File::FILE_ERROR_FAILED; |
| return results; |
| } |
| |
| void SendGetFileInfoResults(GetFileInfoCallback callback, |
| const GetFileInfoResults& results) { |
| std::move(callback).Run(results.error, results.info); |
| } |
| |
| } // namespace |
| |
| std::unique_ptr<FileStreamReader> FileStreamReader::CreateForLocalFile( |
| base::TaskRunner* task_runner, |
| const base::FilePath& file_path, |
| int64_t initial_offset, |
| const base::Time& expected_modification_time) { |
| return base::WrapUnique(new LocalFileStreamReader( |
| task_runner, file_path, initial_offset, expected_modification_time)); |
| } |
| |
| LocalFileStreamReader::~LocalFileStreamReader() = default; |
| |
| int LocalFileStreamReader::Read(net::IOBuffer* buf, |
| int buf_len, |
| net::CompletionOnceCallback callback) { |
| DCHECK(!has_pending_open_); |
| |
| if (stream_impl_) |
| return stream_impl_->Read(buf, buf_len, std::move(callback)); |
| |
| Open(base::BindOnce(&LocalFileStreamReader::DidOpenForRead, |
| weak_factory_.GetWeakPtr(), base::RetainedRef(buf), |
| buf_len, std::move(callback))); |
| |
| return net::ERR_IO_PENDING; |
| } |
| |
| int64_t LocalFileStreamReader::GetLength( |
| net::Int64CompletionOnceCallback callback) { |
| bool posted = base::PostTaskAndReplyWithResult( |
| task_runner_.get(), FROM_HERE, base::BindOnce(&DoGetFileInfo, file_path_), |
| base::BindOnce( |
| &SendGetFileInfoResults, |
| base::BindOnce(&LocalFileStreamReader::DidGetFileInfoForGetLength, |
| weak_factory_.GetWeakPtr(), std::move(callback)))); |
| DCHECK(posted); |
| return net::ERR_IO_PENDING; |
| } |
| |
| LocalFileStreamReader::LocalFileStreamReader( |
| base::TaskRunner* task_runner, |
| const base::FilePath& file_path, |
| int64_t initial_offset, |
| const base::Time& expected_modification_time) |
| : task_runner_(task_runner), |
| file_path_(file_path), |
| initial_offset_(initial_offset), |
| expected_modification_time_(expected_modification_time), |
| has_pending_open_(false), |
| weak_factory_(this) {} |
| |
| void LocalFileStreamReader::Open(net::CompletionOnceCallback callback) { |
| DCHECK(!has_pending_open_); |
| DCHECK(!stream_impl_.get()); |
| has_pending_open_ = true; |
| |
| // Call GetLength first to make it perform last-modified-time verification, |
| // and then call DidVerifyForOpen to do the rest. |
| int64_t verify_result = GetLength( |
| base::BindOnce(&LocalFileStreamReader::DidVerifyForOpen, |
| weak_factory_.GetWeakPtr(), std::move(callback))); |
| DCHECK_EQ(verify_result, net::ERR_IO_PENDING); |
| } |
| |
| void LocalFileStreamReader::DidVerifyForOpen( |
| net::CompletionOnceCallback callback, |
| int64_t get_length_result) { |
| if (get_length_result < 0) { |
| std::move(callback).Run(static_cast<int>(get_length_result)); |
| return; |
| } |
| |
| stream_impl_.reset(new net::FileStream(task_runner_)); |
| callback_ = std::move(callback); |
| const int result = stream_impl_->Open( |
| file_path_, kOpenFlagsForRead, |
| base::BindOnce(&LocalFileStreamReader::DidOpenFileStream, |
| weak_factory_.GetWeakPtr())); |
| if (result != net::ERR_IO_PENDING) |
| std::move(callback_).Run(result); |
| } |
| |
| void LocalFileStreamReader::DidOpenFileStream( |
| int result) { |
| if (result != net::OK) { |
| std::move(callback_).Run(result); |
| return; |
| } |
| result = stream_impl_->Seek( |
| initial_offset_, base::BindOnce(&LocalFileStreamReader::DidSeekFileStream, |
| weak_factory_.GetWeakPtr())); |
| if (result != net::ERR_IO_PENDING) { |
| std::move(callback_).Run(result); |
| } |
| } |
| |
| void LocalFileStreamReader::DidSeekFileStream( |
| int64_t seek_result) { |
| if (seek_result < 0) { |
| std::move(callback_).Run(static_cast<int>(seek_result)); |
| return; |
| } |
| if (seek_result != initial_offset_) { |
| std::move(callback_).Run(net::ERR_REQUEST_RANGE_NOT_SATISFIABLE); |
| return; |
| } |
| std::move(callback_).Run(net::OK); |
| } |
| |
| void LocalFileStreamReader::DidOpenForRead(net::IOBuffer* buf, |
| int buf_len, |
| net::CompletionOnceCallback callback, |
| int open_result) { |
| DCHECK(has_pending_open_); |
| has_pending_open_ = false; |
| if (open_result != net::OK) { |
| stream_impl_.reset(); |
| std::move(callback).Run(open_result); |
| return; |
| } |
| DCHECK(stream_impl_.get()); |
| |
| callback_ = std::move(callback); |
| const int read_result = |
| stream_impl_->Read(buf, buf_len, |
| base::BindOnce(&LocalFileStreamReader::OnRead, |
| weak_factory_.GetWeakPtr())); |
| if (read_result != net::ERR_IO_PENDING) |
| std::move(callback_).Run(read_result); |
| } |
| |
| void LocalFileStreamReader::DidGetFileInfoForGetLength( |
| net::Int64CompletionOnceCallback callback, |
| base::File::Error error, |
| const base::File::Info& file_info) { |
| if (file_info.is_directory) { |
| std::move(callback).Run(net::ERR_FILE_NOT_FOUND); |
| return; |
| } |
| if (error != base::File::FILE_OK) { |
| std::move(callback).Run(net::FileErrorToNetError(error)); |
| return; |
| } |
| if (!VerifySnapshotTime(expected_modification_time_, file_info)) { |
| std::move(callback).Run(net::ERR_UPLOAD_FILE_CHANGED); |
| return; |
| } |
| std::move(callback).Run(file_info.size); |
| } |
| |
| void LocalFileStreamReader::OnRead(int read_result) { |
| std::move(callback_).Run(read_result); |
| } |
| |
| } // namespace storage |