1 //
2 // Copyright (C) 2011 The Android Open Source Project
3 //
4 // Licensed under the Apache License, Version 2.0 (the "License");
5 // you may not use this file except in compliance with the License.
6 // You may obtain a copy of the License at
7 //
8 // http://www.apache.org/licenses/LICENSE-2.0
9 //
10 // Unless required by applicable law or agreed to in writing, software
11 // distributed under the License is distributed on an "AS IS" BASIS,
12 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 // See the License for the specific language governing permissions and
14 // limitations under the License.
15 //
16
17 #include "update_engine/common/download_action.h"
18
19 #include <errno.h>
20
21 #include <algorithm>
22 #include <string>
23
24 #include <base/files/file_path.h>
25 #include <base/metrics/statistics_recorder.h>
26 #include <base/strings/stringprintf.h>
27
28 #include "update_engine/common/action_pipe.h"
29 #include "update_engine/common/boot_control_interface.h"
30 #include "update_engine/common/error_code_utils.h"
31 #include "update_engine/common/multi_range_http_fetcher.h"
32 #include "update_engine/common/prefs_interface.h"
33 #include "update_engine/common/utils.h"
34
35 using base::FilePath;
36 using std::string;
37
38 namespace chromeos_update_engine {
39
DownloadAction(PrefsInterface * prefs,BootControlInterface * boot_control,HardwareInterface * hardware,HttpFetcher * http_fetcher,bool interactive)40 DownloadAction::DownloadAction(PrefsInterface* prefs,
41 BootControlInterface* boot_control,
42 HardwareInterface* hardware,
43 HttpFetcher* http_fetcher,
44 bool interactive)
45 : prefs_(prefs),
46 boot_control_(boot_control),
47 hardware_(hardware),
48 http_fetcher_(new MultiRangeHttpFetcher(http_fetcher)),
49 interactive_(interactive),
50 code_(ErrorCode::kSuccess),
51 delegate_(nullptr) {}
52
~DownloadAction()53 DownloadAction::~DownloadAction() {}
54
PerformAction()55 void DownloadAction::PerformAction() {
56 http_fetcher_->set_delegate(this);
57
58 // Get the InstallPlan and read it
59 CHECK(HasInputObject());
60 install_plan_ = GetInputObject();
61 install_plan_.Dump();
62
63 bytes_received_ = 0;
64 bytes_received_previous_payloads_ = 0;
65 bytes_total_ = 0;
66 for (const auto& payload : install_plan_.payloads)
67 bytes_total_ += payload.size;
68
69 if (install_plan_.is_resume) {
70 int64_t payload_index = 0;
71 if (prefs_->GetInt64(kPrefsUpdateStatePayloadIndex, &payload_index) &&
72 static_cast<size_t>(payload_index) < install_plan_.payloads.size()) {
73 // Save the index for the resume payload before downloading any previous
74 // payload, otherwise it will be overwritten.
75 resume_payload_index_ = payload_index;
76 for (int i = 0; i < payload_index; i++)
77 install_plan_.payloads[i].already_applied = true;
78 }
79 }
80 CHECK_GE(install_plan_.payloads.size(), 1UL);
81 if (!payload_)
82 payload_ = &install_plan_.payloads[0];
83
84 LOG(INFO) << "Marking new slot as unbootable";
85 if (!boot_control_->MarkSlotUnbootable(install_plan_.target_slot)) {
86 LOG(WARNING) << "Unable to mark new slot "
87 << BootControlInterface::SlotName(install_plan_.target_slot)
88 << ". Proceeding with the update anyway.";
89 }
90
91 StartDownloading();
92 }
93
LoadCachedManifest(int64_t manifest_size)94 bool DownloadAction::LoadCachedManifest(int64_t manifest_size) {
95 std::string cached_manifest_bytes;
96 if (!prefs_->GetString(kPrefsManifestBytes, &cached_manifest_bytes) ||
97 cached_manifest_bytes.size() <= 0) {
98 LOG(INFO) << "Cached Manifest data not found";
99 return false;
100 }
101 if (static_cast<int64_t>(cached_manifest_bytes.size()) != manifest_size) {
102 LOG(WARNING) << "Cached metadata has unexpected size: "
103 << cached_manifest_bytes.size() << " vs. " << manifest_size;
104 return false;
105 }
106
107 ErrorCode error;
108 const bool success =
109 delta_performer_->Write(
110 cached_manifest_bytes.data(), cached_manifest_bytes.size(), &error) &&
111 delta_performer_->IsManifestValid();
112 if (success) {
113 LOG(INFO) << "Successfully parsed cached manifest";
114 } else {
115 // If parsing of cached data failed, fall back to fetch them using HTTP
116 LOG(WARNING) << "Cached manifest data fails to load, error code:"
117 << static_cast<int>(error) << "," << error;
118 }
119 return success;
120 }
121
StartDownloading()122 void DownloadAction::StartDownloading() {
123 download_active_ = true;
124 http_fetcher_->ClearRanges();
125
126 if (delta_performer_ != nullptr) {
127 LOG(INFO) << "Using writer for test.";
128 } else {
129 delta_performer_.reset(new DeltaPerformer(prefs_,
130 boot_control_,
131 hardware_,
132 delegate_,
133 &install_plan_,
134 payload_,
135 interactive_));
136 }
137
138 if (install_plan_.is_resume &&
139 payload_ == &install_plan_.payloads[resume_payload_index_]) {
140 // Resuming an update so parse the cached manifest first
141 int64_t manifest_metadata_size = 0;
142 int64_t manifest_signature_size = 0;
143 prefs_->GetInt64(kPrefsManifestMetadataSize, &manifest_metadata_size);
144 prefs_->GetInt64(kPrefsManifestSignatureSize, &manifest_signature_size);
145
146 // TODO(zhangkelvin) Add unittest for success and fallback route
147 if (!LoadCachedManifest(manifest_metadata_size + manifest_signature_size)) {
148 if (delta_performer_) {
149 // Create a new DeltaPerformer to reset all its state
150 delta_performer_ = std::make_unique<DeltaPerformer>(prefs_,
151 boot_control_,
152 hardware_,
153 delegate_,
154 &install_plan_,
155 payload_,
156 interactive_);
157 }
158 http_fetcher_->AddRange(base_offset_,
159 manifest_metadata_size + manifest_signature_size);
160 }
161
162 // If there're remaining unprocessed data blobs, fetch them. Be careful
163 // not to request data beyond the end of the payload to avoid 416 HTTP
164 // response error codes.
165 int64_t next_data_offset = 0;
166 prefs_->GetInt64(kPrefsUpdateStateNextDataOffset, &next_data_offset);
167 uint64_t resume_offset =
168 manifest_metadata_size + manifest_signature_size + next_data_offset;
169 if (!payload_->size) {
170 http_fetcher_->AddRange(base_offset_ + resume_offset);
171 } else if (resume_offset < payload_->size) {
172 http_fetcher_->AddRange(base_offset_ + resume_offset,
173 payload_->size - resume_offset);
174 }
175 } else {
176 if (payload_->size) {
177 http_fetcher_->AddRange(base_offset_, payload_->size);
178 } else {
179 // If no payload size is passed we assume we read until the end of the
180 // stream.
181 http_fetcher_->AddRange(base_offset_);
182 }
183 }
184
185 http_fetcher_->BeginTransfer(install_plan_.download_url);
186 }
187
SuspendAction()188 void DownloadAction::SuspendAction() {
189 http_fetcher_->Pause();
190 }
191
ResumeAction()192 void DownloadAction::ResumeAction() {
193 http_fetcher_->Unpause();
194 }
195
TerminateProcessing()196 void DownloadAction::TerminateProcessing() {
197 if (delta_performer_) {
198 delta_performer_->Close();
199 delta_performer_.reset();
200 }
201 download_active_ = false;
202 // Terminates the transfer. The action is terminated, if necessary, when the
203 // TransferTerminated callback is received.
204 http_fetcher_->TerminateTransfer();
205 }
206
SeekToOffset(off_t offset)207 void DownloadAction::SeekToOffset(off_t offset) {
208 bytes_received_ = offset;
209 }
210
ReceivedBytes(HttpFetcher * fetcher,const void * bytes,size_t length)211 bool DownloadAction::ReceivedBytes(HttpFetcher* fetcher,
212 const void* bytes,
213 size_t length) {
214 bytes_received_ += length;
215 uint64_t bytes_downloaded_total =
216 bytes_received_previous_payloads_ + bytes_received_;
217 if (delegate_ && download_active_) {
218 delegate_->BytesReceived(length, bytes_downloaded_total, bytes_total_);
219 }
220 if (delta_performer_ && !delta_performer_->Write(bytes, length, &code_)) {
221 if (code_ != ErrorCode::kSuccess) {
222 LOG(ERROR) << "Error " << utils::ErrorCodeToString(code_) << " (" << code_
223 << ") in DeltaPerformer's Write method when "
224 << "processing the received payload -- Terminating processing";
225 }
226 // Don't tell the action processor that the action is complete until we get
227 // the TransferTerminated callback. Otherwise, this and the HTTP fetcher
228 // objects may get destroyed before all callbacks are complete.
229 TerminateProcessing();
230 return false;
231 }
232
233 return true;
234 }
235
TransferComplete(HttpFetcher * fetcher,bool successful)236 void DownloadAction::TransferComplete(HttpFetcher* fetcher, bool successful) {
237 if (delta_performer_) {
238 LOG_IF(WARNING, delta_performer_->Close() != 0)
239 << "Error closing the writer.";
240 }
241 download_active_ = false;
242 ErrorCode code =
243 successful ? ErrorCode::kSuccess : ErrorCode::kDownloadTransferError;
244 if (code == ErrorCode::kSuccess) {
245 if (delta_performer_ && !payload_->already_applied)
246 code = delta_performer_->VerifyPayload(payload_->hash, payload_->size);
247 if (code == ErrorCode::kSuccess) {
248 CHECK_EQ(install_plan_.payloads.size(), 1UL);
249 // All payloads have been applied and verified.
250 if (delegate_)
251 delegate_->DownloadComplete();
252
253 // Log UpdateEngine.DownloadAction.* histograms to help diagnose
254 // long-blocking operations.
255 std::string histogram_output;
256 base::StatisticsRecorder::WriteGraph("UpdateEngine.DownloadAction.",
257 &histogram_output);
258 LOG(INFO) << histogram_output;
259 } else {
260 LOG(ERROR) << "Download of " << install_plan_.download_url
261 << " failed due to payload verification error.";
262 }
263 }
264
265 // Write the path to the output pipe if we're successful.
266 if (code == ErrorCode::kSuccess && HasOutputPipe())
267 SetOutputObject(install_plan_);
268 processor_->ActionComplete(this, code);
269 }
270
TransferTerminated(HttpFetcher * fetcher)271 void DownloadAction::TransferTerminated(HttpFetcher* fetcher) {
272 if (code_ != ErrorCode::kSuccess) {
273 processor_->ActionComplete(this, code_);
274 } else if (payload_->already_applied) {
275 LOG(INFO) << "TransferTerminated with ErrorCode::kSuccess when the current "
276 "payload has already applied, treating as TransferComplete.";
277 TransferComplete(fetcher, true);
278 }
279 }
280
281 } // namespace chromeos_update_engine
282