blob: 70156d91357df78e49264891a4f20b66fb087bb6 [file] [log] [blame]
Allie Woodeb9e6d82015-04-17 13:55:30 -07001// Copyright (c) 2012 The Chromium OS Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "update_engine/filesystem_verifier_action.h"
6
7#include <errno.h>
8#include <fcntl.h>
9#include <sys/stat.h>
10#include <sys/types.h>
11
12#include <algorithm>
13#include <cstdlib>
14#include <string>
15
Alex Deymo20c99202015-07-09 16:14:16 -070016#include <base/bind.h>
17#include <base/posix/eintr_wrapper.h>
Allie Woodeb9e6d82015-04-17 13:55:30 -070018
Allie Woodeb9e6d82015-04-17 13:55:30 -070019#include "update_engine/hardware_interface.h"
20#include "update_engine/subprocess.h"
21#include "update_engine/system_state.h"
22#include "update_engine/utils.h"
23
Alex Deymo20c99202015-07-09 16:14:16 -070024using chromeos::MessageLoop;
Allie Woodeb9e6d82015-04-17 13:55:30 -070025using std::string;
26
27namespace chromeos_update_engine {
28
29namespace {
Alex Deymo20c99202015-07-09 16:14:16 -070030const off_t kReadFileBufferSize = 128 * 1024;
Allie Woodeb9e6d82015-04-17 13:55:30 -070031} // namespace
32
33FilesystemVerifierAction::FilesystemVerifierAction(
34 SystemState* system_state,
35 PartitionType partition_type)
36 : partition_type_(partition_type),
Allie Woodeb9e6d82015-04-17 13:55:30 -070037 remaining_size_(kint64max),
38 system_state_(system_state) {}
39
40void FilesystemVerifierAction::PerformAction() {
41 // Will tell the ActionProcessor we've failed if we return.
42 ScopedActionCompleter abort_action_completer(processor_, this);
43
44 if (!HasInputObject()) {
45 LOG(ERROR) << "FilesystemVerifierAction missing input object.";
46 return;
47 }
48 install_plan_ = GetInputObject();
49
50 if (partition_type_ == PartitionType::kKernel) {
51 LOG(INFO) << "verifying kernel, marking as unbootable";
52 if (!system_state_->hardware()->MarkKernelUnbootable(
53 install_plan_.kernel_install_path)) {
54 PLOG(ERROR) << "Unable to clear kernel GPT boot flags: " <<
55 install_plan_.kernel_install_path;
56 }
57 }
58
59 if (install_plan_.is_full_update &&
60 (partition_type_ == PartitionType::kSourceRootfs ||
61 partition_type_ == PartitionType::kSourceKernel)) {
62 // No hash verification needed. Done!
63 LOG(INFO) << "filesystem verifying skipped on full update.";
64 if (HasOutputPipe())
65 SetOutputObject(install_plan_);
66 abort_action_completer.set_code(ErrorCode::kSuccess);
67 return;
68 }
69
70 string target_path;
71 switch (partition_type_) {
72 case PartitionType::kRootfs:
73 target_path = install_plan_.install_path;
74 if (target_path.empty()) {
75 utils::GetInstallDev(system_state_->hardware()->BootDevice(),
76 &target_path);
77 }
78 break;
79 case PartitionType::kKernel:
80 target_path = install_plan_.kernel_install_path;
81 if (target_path.empty()) {
82 string rootfs_path;
83 utils::GetInstallDev(system_state_->hardware()->BootDevice(),
84 &rootfs_path);
85 target_path = utils::KernelDeviceOfBootDevice(rootfs_path);
86 }
87 break;
88 case PartitionType::kSourceRootfs:
89 target_path = install_plan_.source_path.empty() ?
90 system_state_->hardware()->BootDevice() :
91 install_plan_.source_path;
92 break;
93 case PartitionType::kSourceKernel:
94 target_path = install_plan_.kernel_source_path.empty() ?
95 utils::KernelDeviceOfBootDevice(
96 system_state_->hardware()->BootDevice()) :
97 install_plan_.kernel_source_path;
98 break;
99 }
100
Alex Deymo20c99202015-07-09 16:14:16 -0700101 src_fd_ = HANDLE_EINTR(open(target_path.c_str(), O_RDONLY));
102 if (src_fd_ < 0) {
103 PLOG(ERROR) << "Unable to open " << target_path << " for reading";
Allie Woodeb9e6d82015-04-17 13:55:30 -0700104 return;
105 }
106
Alex Deymo20c99202015-07-09 16:14:16 -0700107 DetermineFilesystemSize(src_fd_);
108 buffer_.resize(kReadFileBufferSize);
Allie Woodeb9e6d82015-04-17 13:55:30 -0700109
110 // Start the first read.
Alex Deymo20c99202015-07-09 16:14:16 -0700111 read_task_ = MessageLoop::current()->WatchFileDescriptor(
112 FROM_HERE,
113 src_fd_,
114 MessageLoop::WatchMode::kWatchRead,
115 true, // persistent
116 base::Bind(&FilesystemVerifierAction::OnReadReadyCallback,
117 base::Unretained(this)));
Allie Woodeb9e6d82015-04-17 13:55:30 -0700118
119 abort_action_completer.set_should_complete(false);
120}
121
122void FilesystemVerifierAction::TerminateProcessing() {
Alex Deymo20c99202015-07-09 16:14:16 -0700123 cancelled_ = true;
124 Cleanup(ErrorCode::kSuccess); // error code is ignored if canceled_ is true.
Allie Woodeb9e6d82015-04-17 13:55:30 -0700125}
126
127bool FilesystemVerifierAction::IsCleanupPending() const {
Alex Deymo20c99202015-07-09 16:14:16 -0700128 return (src_fd_ != -1);
Allie Woodeb9e6d82015-04-17 13:55:30 -0700129}
130
131void FilesystemVerifierAction::Cleanup(ErrorCode code) {
Alex Deymo20c99202015-07-09 16:14:16 -0700132 MessageLoop::current()->CancelTask(read_task_);
133 read_task_ = MessageLoop::kTaskIdNull;
134
135 if (src_fd_ != -1) {
136 if (IGNORE_EINTR(close(src_fd_)) != 0) {
137 PLOG(ERROR) << "Error closing fd " << src_fd_;
138 }
139 src_fd_ = -1;
140 }
141 // This memory is not used anymore.
142 buffer_.clear();
143
Allie Woodeb9e6d82015-04-17 13:55:30 -0700144 if (cancelled_)
145 return;
146 if (code == ErrorCode::kSuccess && HasOutputPipe())
147 SetOutputObject(install_plan_);
148 processor_->ActionComplete(this, code);
149}
150
Alex Deymo20c99202015-07-09 16:14:16 -0700151void FilesystemVerifierAction::OnReadReadyCallback() {
152 size_t bytes_to_read = std::min(static_cast<int64_t>(buffer_.size()),
153 remaining_size_);
Allie Woodeb9e6d82015-04-17 13:55:30 -0700154
Alex Deymo20c99202015-07-09 16:14:16 -0700155 ssize_t bytes_read = 0;
156 if (bytes_to_read) {
157 bytes_read = HANDLE_EINTR(read(src_fd_, buffer_.data(), bytes_to_read));
158 }
Allie Woodeb9e6d82015-04-17 13:55:30 -0700159 if (bytes_read < 0) {
Alex Deymo20c99202015-07-09 16:14:16 -0700160 PLOG(ERROR) << "Read failed";
Allie Woodeb9e6d82015-04-17 13:55:30 -0700161 failed_ = true;
162 } else if (bytes_read == 0) {
163 read_done_ = true;
164 } else {
165 remaining_size_ -= bytes_read;
166 }
167
168 if (bytes_read > 0) {
Allie Woodeb9e6d82015-04-17 13:55:30 -0700169 CHECK(!read_done_);
170 if (!hasher_.Update(buffer_.data(), bytes_read)) {
171 LOG(ERROR) << "Unable to update the hash.";
172 failed_ = true;
173 }
174 }
Alex Deymo20c99202015-07-09 16:14:16 -0700175
176 CheckTerminationConditions();
Allie Woodeb9e6d82015-04-17 13:55:30 -0700177}
178
Alex Deymo20c99202015-07-09 16:14:16 -0700179void FilesystemVerifierAction::CheckTerminationConditions() {
Allie Woodeb9e6d82015-04-17 13:55:30 -0700180 if (failed_ || cancelled_) {
181 Cleanup(ErrorCode::kError);
182 return;
183 }
184
Alex Deymo20c99202015-07-09 16:14:16 -0700185 if (read_done_) {
Allie Woodeb9e6d82015-04-17 13:55:30 -0700186 // We're done!
187 ErrorCode code = ErrorCode::kSuccess;
188 if (hasher_.Finalize()) {
189 LOG(INFO) << "Hash: " << hasher_.hash();
190 switch (partition_type_) {
191 case PartitionType::kRootfs:
192 if (install_plan_.rootfs_hash != hasher_.raw_hash()) {
193 code = ErrorCode::kNewRootfsVerificationError;
194 LOG(ERROR) << "New rootfs verification failed.";
195 }
196 break;
197 case PartitionType::kKernel:
198 if (install_plan_.kernel_hash != hasher_.raw_hash()) {
199 code = ErrorCode::kNewKernelVerificationError;
200 LOG(ERROR) << "New kernel verification failed.";
201 }
202 break;
203 case PartitionType::kSourceRootfs:
204 install_plan_.source_rootfs_hash = hasher_.raw_hash();
205 break;
206 case PartitionType::kSourceKernel:
207 install_plan_.source_kernel_hash = hasher_.raw_hash();
208 break;
209 }
210 } else {
211 LOG(ERROR) << "Unable to finalize the hash.";
212 code = ErrorCode::kError;
213 }
214 Cleanup(code);
215 }
216}
217
218void FilesystemVerifierAction::DetermineFilesystemSize(int fd) {
219 switch (partition_type_) {
220 case PartitionType::kRootfs:
221 remaining_size_ = install_plan_.rootfs_size;
222 LOG(INFO) << "Filesystem size: " << remaining_size_ << " bytes.";
223 break;
224 case PartitionType::kKernel:
225 remaining_size_ = install_plan_.kernel_size;
226 LOG(INFO) << "Filesystem size: " << remaining_size_ << " bytes.";
227 break;
228 case PartitionType::kSourceRootfs:
229 {
230 int block_count = 0, block_size = 0;
231 if (utils::GetFilesystemSizeFromFD(fd, &block_count, &block_size)) {
232 remaining_size_ = static_cast<int64_t>(block_count) * block_size;
233 LOG(INFO) << "Filesystem size: " << remaining_size_ << " bytes ("
234 << block_count << "x" << block_size << ").";
235 }
236 }
237 break;
238 default:
239 break;
240 }
241 return;
242}
243
244} // namespace chromeos_update_engine