blob: b913c97a4ac08aef269764dde57df65b61d022f3 [file] [log] [blame]
Alex Deymoaea4c1c2015-08-19 20:24:43 -07001//
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//
rspangler@google.com49fdf182009-10-10 00:57:34 +000016
rspangler@google.com49fdf182009-10-10 00:57:34 +000017#include "update_engine/download_action.h"
Alex Deymoaab50e32014-11-10 19:55:35 -080018
adlr@google.comc98a7ed2009-12-04 18:54:03 +000019#include <errno.h>
20#include <algorithm>
Andrew de los Reyesf9714432010-05-04 10:21:23 -070021#include <string>
22#include <vector>
David Zeuthen8f191b22013-08-06 12:27:50 -070023
Alex Vakulenko75039d72014-03-25 12:36:28 -070024#include <base/files/file_path.h>
25#include <base/strings/stringprintf.h>
David Zeuthen8f191b22013-08-06 12:27:50 -070026
adlr@google.comc98a7ed2009-12-04 18:54:03 +000027#include "update_engine/action_pipe.h"
Gilad Arnold1f847232014-04-07 12:07:49 -070028#include "update_engine/omaha_request_params.h"
David Zeuthen8f191b22013-08-06 12:27:50 -070029#include "update_engine/p2p_manager.h"
Gilad Arnold74b5f552014-10-07 08:17:16 -070030#include "update_engine/payload_state_interface.h"
David Zeuthen34135a92013-08-06 11:16:16 -070031#include "update_engine/utils.h"
adlr@google.comc98a7ed2009-12-04 18:54:03 +000032
Alex Deymof329b932014-10-30 01:37:48 -070033using base::FilePath;
Andrew de los Reyesf9714432010-05-04 10:21:23 -070034using std::string;
35using std::vector;
rspangler@google.com49fdf182009-10-10 00:57:34 +000036
37namespace chromeos_update_engine {
38
Darin Petkov73058b42010-10-06 16:32:19 -070039DownloadAction::DownloadAction(PrefsInterface* prefs,
Jay Srinivasanf0572052012-10-23 18:12:56 -070040 SystemState* system_state,
Darin Petkov73058b42010-10-06 16:32:19 -070041 HttpFetcher* http_fetcher)
42 : prefs_(prefs),
Jay Srinivasanedce2832012-10-24 18:57:47 -070043 system_state_(system_state),
Andrew de los Reyes63b96d72010-05-10 13:08:54 -070044 http_fetcher_(http_fetcher),
Alex Vakulenko88b591f2014-08-28 16:48:57 -070045 writer_(nullptr),
Gilad Arnoldd1c4d2d2014-06-05 14:07:53 -070046 code_(ErrorCode::kSuccess),
Alex Vakulenko88b591f2014-08-28 16:48:57 -070047 delegate_(nullptr),
David Zeuthen8f191b22013-08-06 12:27:50 -070048 bytes_received_(0),
49 p2p_sharing_fd_(-1),
50 p2p_visible_(true) {}
rspangler@google.com49fdf182009-10-10 00:57:34 +000051
52DownloadAction::~DownloadAction() {}
53
David Zeuthen8f191b22013-08-06 12:27:50 -070054void DownloadAction::CloseP2PSharingFd(bool delete_p2p_file) {
55 if (p2p_sharing_fd_ != -1) {
56 if (close(p2p_sharing_fd_) != 0) {
57 PLOG(ERROR) << "Error closing p2p sharing fd";
58 }
59 p2p_sharing_fd_ = -1;
60 }
61
62 if (delete_p2p_file) {
Alex Deymof329b932014-10-30 01:37:48 -070063 FilePath path =
Alex Vakulenko75039d72014-03-25 12:36:28 -070064 system_state_->p2p_manager()->FileGetPath(p2p_file_id_);
David Zeuthen8f191b22013-08-06 12:27:50 -070065 if (unlink(path.value().c_str()) != 0) {
66 PLOG(ERROR) << "Error deleting p2p file " << path.value();
67 } else {
68 LOG(INFO) << "Deleted p2p file " << path.value();
69 }
70 }
71
72 // Don't use p2p from this point onwards.
73 p2p_file_id_.clear();
74}
75
76bool DownloadAction::SetupP2PSharingFd() {
77 P2PManager *p2p_manager = system_state_->p2p_manager();
78
79 if (!p2p_manager->FileShare(p2p_file_id_, install_plan_.payload_size)) {
80 LOG(ERROR) << "Unable to share file via p2p";
Alex Vakulenkod2779df2014-06-16 13:19:00 -070081 CloseP2PSharingFd(true); // delete p2p file
David Zeuthen8f191b22013-08-06 12:27:50 -070082 return false;
83 }
84
85 // File has already been created (and allocated, xattrs been
86 // populated etc.) by FileShare() so just open it for writing.
Alex Deymof329b932014-10-30 01:37:48 -070087 FilePath path = p2p_manager->FileGetPath(p2p_file_id_);
David Zeuthen8f191b22013-08-06 12:27:50 -070088 p2p_sharing_fd_ = open(path.value().c_str(), O_WRONLY);
89 if (p2p_sharing_fd_ == -1) {
90 PLOG(ERROR) << "Error opening file " << path.value();
Alex Vakulenkod2779df2014-06-16 13:19:00 -070091 CloseP2PSharingFd(true); // Delete p2p file.
David Zeuthen8f191b22013-08-06 12:27:50 -070092 return false;
93 }
94
95 // Ensure file to share is world-readable, otherwise
96 // p2p-server and p2p-http-server can't access it.
97 //
98 // (Q: Why doesn't the file have mode 0644 already? A: Because
99 // the process-wide umask is set to 0700 in main.cc.)
100 if (fchmod(p2p_sharing_fd_, 0644) != 0) {
101 PLOG(ERROR) << "Error setting mode 0644 on " << path.value();
Alex Vakulenkod2779df2014-06-16 13:19:00 -0700102 CloseP2PSharingFd(true); // Delete p2p file.
David Zeuthen8f191b22013-08-06 12:27:50 -0700103 return false;
104 }
105
106 // All good.
107 LOG(INFO) << "Writing payload contents to " << path.value();
108 p2p_manager->FileGetVisible(p2p_file_id_, &p2p_visible_);
109 return true;
110}
111
Alex Deymo60ca1a72015-06-18 18:19:15 -0700112void DownloadAction::WriteToP2PFile(const void* data,
David Zeuthen8f191b22013-08-06 12:27:50 -0700113 size_t length,
114 off_t file_offset) {
115 if (p2p_sharing_fd_ == -1) {
116 if (!SetupP2PSharingFd())
117 return;
118 }
119
120 // Check that the file is at least |file_offset| bytes long - if
121 // it's not something is wrong and we must immediately delete the
122 // file to avoid propagating this problem to other peers.
123 //
124 // How can this happen? It could be that we're resuming an update
125 // after a system crash... in this case, it could be that
126 //
127 // 1. the p2p file didn't get properly synced to stable storage; or
128 // 2. the file was deleted at bootup (it's in /var/cache after all); or
129 // 3. other reasons
Gabe Blacka77939e2014-09-09 23:35:08 -0700130 off_t p2p_size = utils::FileSize(p2p_sharing_fd_);
131 if (p2p_size < 0) {
David Zeuthen8f191b22013-08-06 12:27:50 -0700132 PLOG(ERROR) << "Error getting file status for p2p file";
Alex Vakulenkod2779df2014-06-16 13:19:00 -0700133 CloseP2PSharingFd(true); // Delete p2p file.
David Zeuthen8f191b22013-08-06 12:27:50 -0700134 return;
135 }
Gabe Blacka77939e2014-09-09 23:35:08 -0700136 if (p2p_size < file_offset) {
David Zeuthen8f191b22013-08-06 12:27:50 -0700137 LOG(ERROR) << "Wanting to write to file offset " << file_offset
Gabe Blacka77939e2014-09-09 23:35:08 -0700138 << " but existing p2p file is only " << p2p_size
David Zeuthen8f191b22013-08-06 12:27:50 -0700139 << " bytes.";
Alex Vakulenkod2779df2014-06-16 13:19:00 -0700140 CloseP2PSharingFd(true); // Delete p2p file.
David Zeuthen8f191b22013-08-06 12:27:50 -0700141 return;
142 }
143
144 off_t cur_file_offset = lseek(p2p_sharing_fd_, file_offset, SEEK_SET);
145 if (cur_file_offset != static_cast<off_t>(file_offset)) {
146 PLOG(ERROR) << "Error seeking to position "
147 << file_offset << " in p2p file";
Alex Vakulenkod2779df2014-06-16 13:19:00 -0700148 CloseP2PSharingFd(true); // Delete p2p file.
David Zeuthen8f191b22013-08-06 12:27:50 -0700149 } else {
150 // OK, seeking worked, now write the data
151 ssize_t bytes_written = write(p2p_sharing_fd_, data, length);
152 if (bytes_written != static_cast<ssize_t>(length)) {
153 PLOG(ERROR) << "Error writing "
154 << length << " bytes at file offset "
155 << file_offset << " in p2p file";
Alex Vakulenkod2779df2014-06-16 13:19:00 -0700156 CloseP2PSharingFd(true); // Delete p2p file.
David Zeuthen8f191b22013-08-06 12:27:50 -0700157 }
158 }
159}
160
rspangler@google.com49fdf182009-10-10 00:57:34 +0000161void DownloadAction::PerformAction() {
162 http_fetcher_->set_delegate(this);
rspangler@google.com49fdf182009-10-10 00:57:34 +0000163
adlr@google.comc98a7ed2009-12-04 18:54:03 +0000164 // Get the InstallPlan and read it
165 CHECK(HasInputObject());
Andrew de los Reyesf9185172010-05-03 11:07:05 -0700166 install_plan_ = GetInputObject();
Andrew de los Reyes63b96d72010-05-10 13:08:54 -0700167 bytes_received_ = 0;
adlr@google.comc98a7ed2009-12-04 18:54:03 +0000168
Andrew de los Reyesf9185172010-05-03 11:07:05 -0700169 install_plan_.Dump();
adlr@google.comc98a7ed2009-12-04 18:54:03 +0000170
Andrew de los Reyesf9185172010-05-03 11:07:05 -0700171 if (writer_) {
172 LOG(INFO) << "Using writer for test.";
rspangler@google.com49fdf182009-10-10 00:57:34 +0000173 } else {
Jay Srinivasanf0572052012-10-23 18:12:56 -0700174 delta_performer_.reset(new DeltaPerformer(prefs_,
175 system_state_,
176 &install_plan_));
Darin Petkov7ed561b2011-10-04 02:59:03 -0700177 writer_ = delta_performer_.get();
rspangler@google.com49fdf182009-10-10 00:57:34 +0000178 }
Andrew de los Reyesf9185172010-05-03 11:07:05 -0700179 int rc = writer_->Open(install_plan_.install_path.c_str(),
180 O_TRUNC | O_WRONLY | O_CREAT | O_LARGEFILE,
181 0644);
rspangler@google.com49fdf182009-10-10 00:57:34 +0000182 if (rc < 0) {
Andrew de los Reyesf9185172010-05-03 11:07:05 -0700183 LOG(ERROR) << "Unable to open output file " << install_plan_.install_path;
rspangler@google.com49fdf182009-10-10 00:57:34 +0000184 // report error to processor
Gilad Arnoldd1c4d2d2014-06-05 14:07:53 -0700185 processor_->ActionComplete(this, ErrorCode::kInstallDeviceOpenError);
rspangler@google.com49fdf182009-10-10 00:57:34 +0000186 return;
187 }
Darin Petkov7ed561b2011-10-04 02:59:03 -0700188 if (delta_performer_.get() &&
189 !delta_performer_->OpenKernel(
190 install_plan_.kernel_install_path.c_str())) {
191 LOG(ERROR) << "Unable to open kernel file "
192 << install_plan_.kernel_install_path.c_str();
193 writer_->Close();
Gilad Arnoldd1c4d2d2014-06-05 14:07:53 -0700194 processor_->ActionComplete(this, ErrorCode::kKernelDeviceOpenError);
Darin Petkov7ed561b2011-10-04 02:59:03 -0700195 return;
Andrew de los Reyesf9185172010-05-03 11:07:05 -0700196 }
Darin Petkov9d911fa2010-08-19 09:36:08 -0700197 if (delegate_) {
198 delegate_->SetDownloadStatus(true); // Set to active.
199 }
David Zeuthen8f191b22013-08-06 12:27:50 -0700200
Alex Vakulenko88b591f2014-08-28 16:48:57 -0700201 if (system_state_ != nullptr) {
Gilad Arnold74b5f552014-10-07 08:17:16 -0700202 const PayloadStateInterface* payload_state = system_state_->payload_state();
David Zeuthen8f191b22013-08-06 12:27:50 -0700203 string file_id = utils::CalculateP2PFileId(install_plan_.payload_hash,
204 install_plan_.payload_size);
Gilad Arnold74b5f552014-10-07 08:17:16 -0700205 if (payload_state->GetUsingP2PForSharing()) {
David Zeuthen8f191b22013-08-06 12:27:50 -0700206 // If we're sharing the update, store the file_id to convey
207 // that we should write to the file.
208 p2p_file_id_ = file_id;
209 LOG(INFO) << "p2p file id: " << p2p_file_id_;
210 } else {
211 // Even if we're not sharing the update, it could be that
212 // there's a partial file from a previous attempt with the same
213 // hash. If this is the case, we NEED to clean it up otherwise
214 // we're essentially timing out other peers downloading from us
215 // (since we're never going to complete the file).
Alex Deymof329b932014-10-30 01:37:48 -0700216 FilePath path = system_state_->p2p_manager()->FileGetPath(file_id);
David Zeuthen8f191b22013-08-06 12:27:50 -0700217 if (!path.empty()) {
218 if (unlink(path.value().c_str()) != 0) {
219 PLOG(ERROR) << "Error deleting p2p file " << path.value();
220 } else {
221 LOG(INFO) << "Deleting partial p2p file " << path.value()
222 << " since we're not using p2p to share.";
223 }
224 }
225 }
David Zeuthen8f191b22013-08-06 12:27:50 -0700226
Gilad Arnold74b5f552014-10-07 08:17:16 -0700227 // Tweak timeouts on the HTTP fetcher if we're downloading from a
228 // local peer.
229 if (payload_state->GetUsingP2PForDownloading() &&
230 payload_state->GetP2PUrl() == install_plan_.download_url) {
231 LOG(INFO) << "Tweaking HTTP fetcher since we're downloading via p2p";
232 http_fetcher_->set_low_speed_limit(kDownloadP2PLowSpeedLimitBps,
233 kDownloadP2PLowSpeedTimeSeconds);
234 http_fetcher_->set_max_retry_count(kDownloadP2PMaxRetryCount);
235 http_fetcher_->set_connect_timeout(kDownloadP2PConnectTimeoutSeconds);
236 }
David Zeuthen34135a92013-08-06 11:16:16 -0700237 }
238
Andrew de los Reyesf9185172010-05-03 11:07:05 -0700239 http_fetcher_->BeginTransfer(install_plan_.download_url);
rspangler@google.com49fdf182009-10-10 00:57:34 +0000240}
241
242void DownloadAction::TerminateProcessing() {
Darin Petkov698d0412010-10-13 10:59:44 -0700243 if (writer_) {
Jay Srinivasan1c0fe792013-03-28 16:45:25 -0700244 writer_->Close();
Alex Vakulenko88b591f2014-08-28 16:48:57 -0700245 writer_ = nullptr;
Darin Petkov698d0412010-10-13 10:59:44 -0700246 }
Darin Petkov9d911fa2010-08-19 09:36:08 -0700247 if (delegate_) {
248 delegate_->SetDownloadStatus(false); // Set to inactive.
249 }
Alex Vakulenkod2779df2014-06-16 13:19:00 -0700250 CloseP2PSharingFd(false); // Keep p2p file.
Darin Petkov9ce452b2010-11-17 14:33:28 -0800251 // Terminates the transfer. The action is terminated, if necessary, when the
252 // TransferTerminated callback is received.
253 http_fetcher_->TerminateTransfer();
rspangler@google.com49fdf182009-10-10 00:57:34 +0000254}
255
Andrew de los Reyes34e41a12010-10-26 20:07:58 -0700256void DownloadAction::SeekToOffset(off_t offset) {
257 bytes_received_ = offset;
258}
259
Alex Deymo60ca1a72015-06-18 18:19:15 -0700260void DownloadAction::ReceivedBytes(HttpFetcher* fetcher,
Alex Vakulenkof68bbbc2015-02-09 12:53:18 -0800261 const void* bytes,
262 size_t length) {
David Zeuthen8f191b22013-08-06 12:27:50 -0700263 // Note that bytes_received_ is the current offset.
264 if (!p2p_file_id_.empty()) {
265 WriteToP2PFile(bytes, length, bytes_received_);
266 }
267
Andrew de los Reyes63b96d72010-05-10 13:08:54 -0700268 bytes_received_ += length;
269 if (delegate_)
Jay Srinivasan51dcf262012-09-13 17:24:32 -0700270 delegate_->BytesReceived(bytes_received_, install_plan_.payload_size);
271 if (writer_ && !writer_->Write(bytes, length, &code_)) {
272 LOG(ERROR) << "Error " << code_ << " in DeltaPerformer's Write method when "
273 << "processing the received payload -- Terminating processing";
David Zeuthen69bc2732013-11-26 16:08:21 -0800274 // Delete p2p file, if applicable.
275 if (!p2p_file_id_.empty())
276 CloseP2PSharingFd(true);
Darin Petkov9ce452b2010-11-17 14:33:28 -0800277 // Don't tell the action processor that the action is complete until we get
278 // the TransferTerminated callback. Otherwise, this and the HTTP fetcher
279 // objects may get destroyed before all callbacks are complete.
Darin Petkov698d0412010-10-13 10:59:44 -0700280 TerminateProcessing();
Darin Petkov698d0412010-10-13 10:59:44 -0700281 return;
282 }
David Zeuthen8f191b22013-08-06 12:27:50 -0700283
284 // Call p2p_manager_->FileMakeVisible() when we've successfully
285 // verified the manifest!
286 if (!p2p_visible_ &&
287 delta_performer_.get() && delta_performer_->IsManifestValid()) {
288 LOG(INFO) << "Manifest has been validated. Making p2p file visible.";
289 system_state_->p2p_manager()->FileMakeVisible(p2p_file_id_);
290 p2p_visible_ = true;
291 }
rspangler@google.com49fdf182009-10-10 00:57:34 +0000292}
293
Alex Deymo60ca1a72015-06-18 18:19:15 -0700294void DownloadAction::TransferComplete(HttpFetcher* fetcher, bool successful) {
rspangler@google.com49fdf182009-10-10 00:57:34 +0000295 if (writer_) {
Darin Petkov698d0412010-10-13 10:59:44 -0700296 LOG_IF(WARNING, writer_->Close() != 0) << "Error closing the writer.";
Alex Vakulenko88b591f2014-08-28 16:48:57 -0700297 writer_ = nullptr;
rspangler@google.com49fdf182009-10-10 00:57:34 +0000298 }
Darin Petkov9d911fa2010-08-19 09:36:08 -0700299 if (delegate_) {
300 delegate_->SetDownloadStatus(false); // Set to inactive.
301 }
David Zeuthena99981f2013-04-29 13:42:47 -0700302 ErrorCode code =
Gilad Arnoldd1c4d2d2014-06-05 14:07:53 -0700303 successful ? ErrorCode::kSuccess : ErrorCode::kDownloadTransferError;
304 if (code == ErrorCode::kSuccess && delta_performer_.get()) {
Jay Srinivasan51dcf262012-09-13 17:24:32 -0700305 code = delta_performer_->VerifyPayload(install_plan_.payload_hash,
306 install_plan_.payload_size);
Gilad Arnoldd1c4d2d2014-06-05 14:07:53 -0700307 if (code != ErrorCode::kSuccess) {
Darin Petkov7ed561b2011-10-04 02:59:03 -0700308 LOG(ERROR) << "Download of " << install_plan_.download_url
309 << " failed due to payload verification error.";
David Zeuthen69bc2732013-11-26 16:08:21 -0800310 // Delete p2p file, if applicable.
311 if (!p2p_file_id_.empty())
312 CloseP2PSharingFd(true);
Darin Petkov7ed561b2011-10-04 02:59:03 -0700313 } else if (!delta_performer_->GetNewPartitionInfo(
314 &install_plan_.kernel_size,
315 &install_plan_.kernel_hash,
316 &install_plan_.rootfs_size,
317 &install_plan_.rootfs_hash)) {
318 LOG(ERROR) << "Unable to get new partition hash info.";
Gilad Arnoldd1c4d2d2014-06-05 14:07:53 -0700319 code = ErrorCode::kDownloadNewPartitionInfoError;
rspangler@google.com49fdf182009-10-10 00:57:34 +0000320 }
321 }
Darin Petkovc1a8b422010-07-19 11:34:49 -0700322
Darin Petkovc97435c2010-07-20 12:37:43 -0700323 // Write the path to the output pipe if we're successful.
Gilad Arnoldd1c4d2d2014-06-05 14:07:53 -0700324 if (code == ErrorCode::kSuccess && HasOutputPipe())
Darin Petkov3aefa862010-12-07 14:45:00 -0800325 SetOutputObject(install_plan_);
Darin Petkovc97435c2010-07-20 12:37:43 -0700326 processor_->ActionComplete(this, code);
rspangler@google.com49fdf182009-10-10 00:57:34 +0000327}
328
Darin Petkov9ce452b2010-11-17 14:33:28 -0800329void DownloadAction::TransferTerminated(HttpFetcher *fetcher) {
Gilad Arnoldd1c4d2d2014-06-05 14:07:53 -0700330 if (code_ != ErrorCode::kSuccess) {
Darin Petkov9ce452b2010-11-17 14:33:28 -0800331 processor_->ActionComplete(this, code_);
332 }
333}
334
Alex Vakulenkod2779df2014-06-16 13:19:00 -0700335} // namespace chromeos_update_engine