1*5a923131SAndroid Build Coastguard Worker //
2*5a923131SAndroid Build Coastguard Worker // Copyright (C) 2016 The Android Open Source Project
3*5a923131SAndroid Build Coastguard Worker //
4*5a923131SAndroid Build Coastguard Worker // Licensed under the Apache License, Version 2.0 (the "License");
5*5a923131SAndroid Build Coastguard Worker // you may not use this file except in compliance with the License.
6*5a923131SAndroid Build Coastguard Worker // You may obtain a copy of the License at
7*5a923131SAndroid Build Coastguard Worker //
8*5a923131SAndroid Build Coastguard Worker // http://www.apache.org/licenses/LICENSE-2.0
9*5a923131SAndroid Build Coastguard Worker //
10*5a923131SAndroid Build Coastguard Worker // Unless required by applicable law or agreed to in writing, software
11*5a923131SAndroid Build Coastguard Worker // distributed under the License is distributed on an "AS IS" BASIS,
12*5a923131SAndroid Build Coastguard Worker // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13*5a923131SAndroid Build Coastguard Worker // See the License for the specific language governing permissions and
14*5a923131SAndroid Build Coastguard Worker // limitations under the License.
15*5a923131SAndroid Build Coastguard Worker //
16*5a923131SAndroid Build Coastguard Worker
17*5a923131SAndroid Build Coastguard Worker #include "update_engine/common/file_fetcher.h"
18*5a923131SAndroid Build Coastguard Worker
19*5a923131SAndroid Build Coastguard Worker #include <algorithm>
20*5a923131SAndroid Build Coastguard Worker #include <string>
21*5a923131SAndroid Build Coastguard Worker
22*5a923131SAndroid Build Coastguard Worker #include <base/bind.h>
23*5a923131SAndroid Build Coastguard Worker #include <base/format_macros.h>
24*5a923131SAndroid Build Coastguard Worker #include <base/location.h>
25*5a923131SAndroid Build Coastguard Worker #include <base/logging.h>
26*5a923131SAndroid Build Coastguard Worker #include <android-base/stringprintf.h>
27*5a923131SAndroid Build Coastguard Worker #include <brillo/streams/file_stream.h>
28*5a923131SAndroid Build Coastguard Worker
29*5a923131SAndroid Build Coastguard Worker #include "update_engine/common/utils.h"
30*5a923131SAndroid Build Coastguard Worker
31*5a923131SAndroid Build Coastguard Worker using std::string;
32*5a923131SAndroid Build Coastguard Worker
33*5a923131SAndroid Build Coastguard Worker namespace {
34*5a923131SAndroid Build Coastguard Worker
35*5a923131SAndroid Build Coastguard Worker size_t kReadBufferSize = 16 * 1024;
36*5a923131SAndroid Build Coastguard Worker
37*5a923131SAndroid Build Coastguard Worker } // namespace
38*5a923131SAndroid Build Coastguard Worker
39*5a923131SAndroid Build Coastguard Worker namespace chromeos_update_engine {
40*5a923131SAndroid Build Coastguard Worker
41*5a923131SAndroid Build Coastguard Worker // static
SupportedUrl(const string & url)42*5a923131SAndroid Build Coastguard Worker bool FileFetcher::SupportedUrl(const string& url) {
43*5a923131SAndroid Build Coastguard Worker // Note that we require the file path to start with a "/".
44*5a923131SAndroid Build Coastguard Worker return (android::base::StartsWith(ToLower(url), "file:///") ||
45*5a923131SAndroid Build Coastguard Worker android::base::StartsWith(ToLower(url), "fd://"));
46*5a923131SAndroid Build Coastguard Worker }
47*5a923131SAndroid Build Coastguard Worker
~FileFetcher()48*5a923131SAndroid Build Coastguard Worker FileFetcher::~FileFetcher() {
49*5a923131SAndroid Build Coastguard Worker LOG_IF(ERROR, transfer_in_progress_)
50*5a923131SAndroid Build Coastguard Worker << "Destroying the fetcher while a transfer is in progress.";
51*5a923131SAndroid Build Coastguard Worker CleanUp();
52*5a923131SAndroid Build Coastguard Worker }
53*5a923131SAndroid Build Coastguard Worker
54*5a923131SAndroid Build Coastguard Worker // Begins the transfer, which must not have already been started.
BeginTransfer(const string & url)55*5a923131SAndroid Build Coastguard Worker void FileFetcher::BeginTransfer(const string& url) {
56*5a923131SAndroid Build Coastguard Worker CHECK(!transfer_in_progress_);
57*5a923131SAndroid Build Coastguard Worker
58*5a923131SAndroid Build Coastguard Worker if (!SupportedUrl(url)) {
59*5a923131SAndroid Build Coastguard Worker LOG(ERROR) << "Unsupported file URL: " << url;
60*5a923131SAndroid Build Coastguard Worker // No HTTP error code when the URL is not supported.
61*5a923131SAndroid Build Coastguard Worker http_response_code_ = 0;
62*5a923131SAndroid Build Coastguard Worker CleanUp();
63*5a923131SAndroid Build Coastguard Worker if (delegate_)
64*5a923131SAndroid Build Coastguard Worker delegate_->TransferComplete(this, false);
65*5a923131SAndroid Build Coastguard Worker return;
66*5a923131SAndroid Build Coastguard Worker }
67*5a923131SAndroid Build Coastguard Worker
68*5a923131SAndroid Build Coastguard Worker string file_path;
69*5a923131SAndroid Build Coastguard Worker
70*5a923131SAndroid Build Coastguard Worker if (android::base::StartsWith(ToLower(url), "fd://")) {
71*5a923131SAndroid Build Coastguard Worker int fd = std::stoi(url.substr(strlen("fd://")));
72*5a923131SAndroid Build Coastguard Worker file_path = url;
73*5a923131SAndroid Build Coastguard Worker stream_ = brillo::FileStream::FromFileDescriptor(fd, false, nullptr);
74*5a923131SAndroid Build Coastguard Worker } else {
75*5a923131SAndroid Build Coastguard Worker file_path = url.substr(strlen("file://"));
76*5a923131SAndroid Build Coastguard Worker stream_ =
77*5a923131SAndroid Build Coastguard Worker brillo::FileStream::Open(base::FilePath(file_path),
78*5a923131SAndroid Build Coastguard Worker brillo::Stream::AccessMode::READ,
79*5a923131SAndroid Build Coastguard Worker brillo::FileStream::Disposition::OPEN_EXISTING,
80*5a923131SAndroid Build Coastguard Worker nullptr);
81*5a923131SAndroid Build Coastguard Worker }
82*5a923131SAndroid Build Coastguard Worker
83*5a923131SAndroid Build Coastguard Worker if (!stream_) {
84*5a923131SAndroid Build Coastguard Worker LOG(ERROR) << "Couldn't open " << file_path;
85*5a923131SAndroid Build Coastguard Worker http_response_code_ = kHttpResponseNotFound;
86*5a923131SAndroid Build Coastguard Worker CleanUp();
87*5a923131SAndroid Build Coastguard Worker if (delegate_)
88*5a923131SAndroid Build Coastguard Worker delegate_->TransferComplete(this, false);
89*5a923131SAndroid Build Coastguard Worker return;
90*5a923131SAndroid Build Coastguard Worker }
91*5a923131SAndroid Build Coastguard Worker http_response_code_ = kHttpResponseOk;
92*5a923131SAndroid Build Coastguard Worker
93*5a923131SAndroid Build Coastguard Worker if (offset_)
94*5a923131SAndroid Build Coastguard Worker stream_->SetPosition(offset_, nullptr);
95*5a923131SAndroid Build Coastguard Worker bytes_copied_ = 0;
96*5a923131SAndroid Build Coastguard Worker transfer_in_progress_ = true;
97*5a923131SAndroid Build Coastguard Worker ScheduleRead();
98*5a923131SAndroid Build Coastguard Worker }
99*5a923131SAndroid Build Coastguard Worker
TerminateTransfer()100*5a923131SAndroid Build Coastguard Worker void FileFetcher::TerminateTransfer() {
101*5a923131SAndroid Build Coastguard Worker CleanUp();
102*5a923131SAndroid Build Coastguard Worker if (delegate_) {
103*5a923131SAndroid Build Coastguard Worker // Note that after the callback returns this object may be destroyed.
104*5a923131SAndroid Build Coastguard Worker delegate_->TransferTerminated(this);
105*5a923131SAndroid Build Coastguard Worker }
106*5a923131SAndroid Build Coastguard Worker }
107*5a923131SAndroid Build Coastguard Worker
ScheduleRead()108*5a923131SAndroid Build Coastguard Worker void FileFetcher::ScheduleRead() {
109*5a923131SAndroid Build Coastguard Worker if (transfer_paused_ || ongoing_read_ || !transfer_in_progress_)
110*5a923131SAndroid Build Coastguard Worker return;
111*5a923131SAndroid Build Coastguard Worker
112*5a923131SAndroid Build Coastguard Worker buffer_.resize(kReadBufferSize);
113*5a923131SAndroid Build Coastguard Worker size_t bytes_to_read = buffer_.size();
114*5a923131SAndroid Build Coastguard Worker if (data_length_ >= 0) {
115*5a923131SAndroid Build Coastguard Worker bytes_to_read = std::min(static_cast<uint64_t>(bytes_to_read),
116*5a923131SAndroid Build Coastguard Worker data_length_ - bytes_copied_);
117*5a923131SAndroid Build Coastguard Worker }
118*5a923131SAndroid Build Coastguard Worker
119*5a923131SAndroid Build Coastguard Worker if (!bytes_to_read) {
120*5a923131SAndroid Build Coastguard Worker OnReadDoneCallback(0);
121*5a923131SAndroid Build Coastguard Worker return;
122*5a923131SAndroid Build Coastguard Worker }
123*5a923131SAndroid Build Coastguard Worker
124*5a923131SAndroid Build Coastguard Worker ongoing_read_ = stream_->ReadAsync(
125*5a923131SAndroid Build Coastguard Worker buffer_.data(),
126*5a923131SAndroid Build Coastguard Worker bytes_to_read,
127*5a923131SAndroid Build Coastguard Worker base::Bind(&FileFetcher::OnReadDoneCallback, base::Unretained(this)),
128*5a923131SAndroid Build Coastguard Worker base::Bind(&FileFetcher::OnReadErrorCallback, base::Unretained(this)),
129*5a923131SAndroid Build Coastguard Worker nullptr);
130*5a923131SAndroid Build Coastguard Worker
131*5a923131SAndroid Build Coastguard Worker if (!ongoing_read_) {
132*5a923131SAndroid Build Coastguard Worker LOG(ERROR) << "Unable to schedule an asynchronous read from the stream.";
133*5a923131SAndroid Build Coastguard Worker CleanUp();
134*5a923131SAndroid Build Coastguard Worker if (delegate_)
135*5a923131SAndroid Build Coastguard Worker delegate_->TransferComplete(this, false);
136*5a923131SAndroid Build Coastguard Worker }
137*5a923131SAndroid Build Coastguard Worker }
138*5a923131SAndroid Build Coastguard Worker
OnReadDoneCallback(size_t bytes_read)139*5a923131SAndroid Build Coastguard Worker void FileFetcher::OnReadDoneCallback(size_t bytes_read) {
140*5a923131SAndroid Build Coastguard Worker ongoing_read_ = false;
141*5a923131SAndroid Build Coastguard Worker if (bytes_read == 0) {
142*5a923131SAndroid Build Coastguard Worker CleanUp();
143*5a923131SAndroid Build Coastguard Worker if (delegate_)
144*5a923131SAndroid Build Coastguard Worker delegate_->TransferComplete(this, true);
145*5a923131SAndroid Build Coastguard Worker } else {
146*5a923131SAndroid Build Coastguard Worker bytes_copied_ += bytes_read;
147*5a923131SAndroid Build Coastguard Worker if (delegate_ &&
148*5a923131SAndroid Build Coastguard Worker !delegate_->ReceivedBytes(this, buffer_.data(), bytes_read))
149*5a923131SAndroid Build Coastguard Worker return;
150*5a923131SAndroid Build Coastguard Worker ScheduleRead();
151*5a923131SAndroid Build Coastguard Worker }
152*5a923131SAndroid Build Coastguard Worker }
153*5a923131SAndroid Build Coastguard Worker
OnReadErrorCallback(const brillo::Error * error)154*5a923131SAndroid Build Coastguard Worker void FileFetcher::OnReadErrorCallback(const brillo::Error* error) {
155*5a923131SAndroid Build Coastguard Worker LOG(ERROR) << "Asynchronous read failed: " << error->GetMessage();
156*5a923131SAndroid Build Coastguard Worker CleanUp();
157*5a923131SAndroid Build Coastguard Worker if (delegate_)
158*5a923131SAndroid Build Coastguard Worker delegate_->TransferComplete(this, false);
159*5a923131SAndroid Build Coastguard Worker }
160*5a923131SAndroid Build Coastguard Worker
Pause()161*5a923131SAndroid Build Coastguard Worker void FileFetcher::Pause() {
162*5a923131SAndroid Build Coastguard Worker if (transfer_paused_) {
163*5a923131SAndroid Build Coastguard Worker LOG(ERROR) << "Fetcher already paused.";
164*5a923131SAndroid Build Coastguard Worker return;
165*5a923131SAndroid Build Coastguard Worker }
166*5a923131SAndroid Build Coastguard Worker transfer_paused_ = true;
167*5a923131SAndroid Build Coastguard Worker }
168*5a923131SAndroid Build Coastguard Worker
Unpause()169*5a923131SAndroid Build Coastguard Worker void FileFetcher::Unpause() {
170*5a923131SAndroid Build Coastguard Worker if (!transfer_paused_) {
171*5a923131SAndroid Build Coastguard Worker LOG(ERROR) << "Resume attempted when fetcher not paused.";
172*5a923131SAndroid Build Coastguard Worker return;
173*5a923131SAndroid Build Coastguard Worker }
174*5a923131SAndroid Build Coastguard Worker transfer_paused_ = false;
175*5a923131SAndroid Build Coastguard Worker ScheduleRead();
176*5a923131SAndroid Build Coastguard Worker }
177*5a923131SAndroid Build Coastguard Worker
CleanUp()178*5a923131SAndroid Build Coastguard Worker void FileFetcher::CleanUp() {
179*5a923131SAndroid Build Coastguard Worker if (stream_) {
180*5a923131SAndroid Build Coastguard Worker stream_->CancelPendingAsyncOperations();
181*5a923131SAndroid Build Coastguard Worker stream_->CloseBlocking(nullptr);
182*5a923131SAndroid Build Coastguard Worker stream_.reset();
183*5a923131SAndroid Build Coastguard Worker }
184*5a923131SAndroid Build Coastguard Worker // Destroying the |stream_| releases the callback, so we don't have any
185*5a923131SAndroid Build Coastguard Worker // ongoing read at this point.
186*5a923131SAndroid Build Coastguard Worker ongoing_read_ = false;
187*5a923131SAndroid Build Coastguard Worker buffer_ = brillo::Blob();
188*5a923131SAndroid Build Coastguard Worker
189*5a923131SAndroid Build Coastguard Worker transfer_in_progress_ = false;
190*5a923131SAndroid Build Coastguard Worker transfer_paused_ = false;
191*5a923131SAndroid Build Coastguard Worker }
192*5a923131SAndroid Build Coastguard Worker } // namespace chromeos_update_engine
193