blob: 5c352b07d5f1c976916b971ce92a6088873c4366 [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"
Allie Woodeb9e6d82015-04-17 13:55:30 -070020#include "update_engine/system_state.h"
21#include "update_engine/utils.h"
22
Alex Deymo20c99202015-07-09 16:14:16 -070023using chromeos::MessageLoop;
Allie Woodeb9e6d82015-04-17 13:55:30 -070024using std::string;
25
26namespace chromeos_update_engine {
27
28namespace {
Alex Deymo20c99202015-07-09 16:14:16 -070029const off_t kReadFileBufferSize = 128 * 1024;
Allie Woodeb9e6d82015-04-17 13:55:30 -070030} // namespace
31
32FilesystemVerifierAction::FilesystemVerifierAction(
33 SystemState* system_state,
34 PartitionType partition_type)
35 : partition_type_(partition_type),
Allie Woodeb9e6d82015-04-17 13:55:30 -070036 remaining_size_(kint64max),
37 system_state_(system_state) {}
38
39void FilesystemVerifierAction::PerformAction() {
40 // Will tell the ActionProcessor we've failed if we return.
41 ScopedActionCompleter abort_action_completer(processor_, this);
42
43 if (!HasInputObject()) {
44 LOG(ERROR) << "FilesystemVerifierAction missing input object.";
45 return;
46 }
47 install_plan_ = GetInputObject();
48
49 if (partition_type_ == PartitionType::kKernel) {
50 LOG(INFO) << "verifying kernel, marking as unbootable";
51 if (!system_state_->hardware()->MarkKernelUnbootable(
52 install_plan_.kernel_install_path)) {
53 PLOG(ERROR) << "Unable to clear kernel GPT boot flags: " <<
54 install_plan_.kernel_install_path;
55 }
56 }
57
58 if (install_plan_.is_full_update &&
59 (partition_type_ == PartitionType::kSourceRootfs ||
60 partition_type_ == PartitionType::kSourceKernel)) {
61 // No hash verification needed. Done!
62 LOG(INFO) << "filesystem verifying skipped on full update.";
63 if (HasOutputPipe())
64 SetOutputObject(install_plan_);
65 abort_action_completer.set_code(ErrorCode::kSuccess);
66 return;
67 }
68
69 string target_path;
70 switch (partition_type_) {
71 case PartitionType::kRootfs:
72 target_path = install_plan_.install_path;
73 if (target_path.empty()) {
74 utils::GetInstallDev(system_state_->hardware()->BootDevice(),
75 &target_path);
76 }
77 break;
78 case PartitionType::kKernel:
79 target_path = install_plan_.kernel_install_path;
80 if (target_path.empty()) {
81 string rootfs_path;
82 utils::GetInstallDev(system_state_->hardware()->BootDevice(),
83 &rootfs_path);
84 target_path = utils::KernelDeviceOfBootDevice(rootfs_path);
85 }
86 break;
87 case PartitionType::kSourceRootfs:
88 target_path = install_plan_.source_path.empty() ?
89 system_state_->hardware()->BootDevice() :
90 install_plan_.source_path;
91 break;
92 case PartitionType::kSourceKernel:
93 target_path = install_plan_.kernel_source_path.empty() ?
94 utils::KernelDeviceOfBootDevice(
95 system_state_->hardware()->BootDevice()) :
96 install_plan_.kernel_source_path;
97 break;
98 }
99
Alex Deymo20c99202015-07-09 16:14:16 -0700100 src_fd_ = HANDLE_EINTR(open(target_path.c_str(), O_RDONLY));
101 if (src_fd_ < 0) {
102 PLOG(ERROR) << "Unable to open " << target_path << " for reading";
Allie Woodeb9e6d82015-04-17 13:55:30 -0700103 return;
104 }
105
Alex Deymo20c99202015-07-09 16:14:16 -0700106 DetermineFilesystemSize(src_fd_);
107 buffer_.resize(kReadFileBufferSize);
Allie Woodeb9e6d82015-04-17 13:55:30 -0700108
109 // Start the first read.
Alex Deymo20c99202015-07-09 16:14:16 -0700110 read_task_ = MessageLoop::current()->WatchFileDescriptor(
111 FROM_HERE,
112 src_fd_,
113 MessageLoop::WatchMode::kWatchRead,
114 true, // persistent
115 base::Bind(&FilesystemVerifierAction::OnReadReadyCallback,
116 base::Unretained(this)));
Allie Woodeb9e6d82015-04-17 13:55:30 -0700117
118 abort_action_completer.set_should_complete(false);
119}
120
121void FilesystemVerifierAction::TerminateProcessing() {
Alex Deymo20c99202015-07-09 16:14:16 -0700122 cancelled_ = true;
123 Cleanup(ErrorCode::kSuccess); // error code is ignored if canceled_ is true.
Allie Woodeb9e6d82015-04-17 13:55:30 -0700124}
125
126bool FilesystemVerifierAction::IsCleanupPending() const {
Alex Deymo20c99202015-07-09 16:14:16 -0700127 return (src_fd_ != -1);
Allie Woodeb9e6d82015-04-17 13:55:30 -0700128}
129
130void FilesystemVerifierAction::Cleanup(ErrorCode code) {
Alex Deymo20c99202015-07-09 16:14:16 -0700131 MessageLoop::current()->CancelTask(read_task_);
132 read_task_ = MessageLoop::kTaskIdNull;
133
134 if (src_fd_ != -1) {
135 if (IGNORE_EINTR(close(src_fd_)) != 0) {
136 PLOG(ERROR) << "Error closing fd " << src_fd_;
137 }
138 src_fd_ = -1;
139 }
140 // This memory is not used anymore.
141 buffer_.clear();
142
Allie Woodeb9e6d82015-04-17 13:55:30 -0700143 if (cancelled_)
144 return;
145 if (code == ErrorCode::kSuccess && HasOutputPipe())
146 SetOutputObject(install_plan_);
147 processor_->ActionComplete(this, code);
148}
149
Alex Deymo20c99202015-07-09 16:14:16 -0700150void FilesystemVerifierAction::OnReadReadyCallback() {
151 size_t bytes_to_read = std::min(static_cast<int64_t>(buffer_.size()),
152 remaining_size_);
Allie Woodeb9e6d82015-04-17 13:55:30 -0700153
Alex Deymo20c99202015-07-09 16:14:16 -0700154 ssize_t bytes_read = 0;
155 if (bytes_to_read) {
156 bytes_read = HANDLE_EINTR(read(src_fd_, buffer_.data(), bytes_to_read));
157 }
Allie Woodeb9e6d82015-04-17 13:55:30 -0700158 if (bytes_read < 0) {
Alex Deymo20c99202015-07-09 16:14:16 -0700159 PLOG(ERROR) << "Read failed";
Allie Woodeb9e6d82015-04-17 13:55:30 -0700160 failed_ = true;
161 } else if (bytes_read == 0) {
162 read_done_ = true;
163 } else {
164 remaining_size_ -= bytes_read;
165 }
166
167 if (bytes_read > 0) {
Allie Woodeb9e6d82015-04-17 13:55:30 -0700168 CHECK(!read_done_);
169 if (!hasher_.Update(buffer_.data(), bytes_read)) {
170 LOG(ERROR) << "Unable to update the hash.";
171 failed_ = true;
172 }
173 }
Alex Deymo20c99202015-07-09 16:14:16 -0700174
175 CheckTerminationConditions();
Allie Woodeb9e6d82015-04-17 13:55:30 -0700176}
177
Alex Deymo20c99202015-07-09 16:14:16 -0700178void FilesystemVerifierAction::CheckTerminationConditions() {
Allie Woodeb9e6d82015-04-17 13:55:30 -0700179 if (failed_ || cancelled_) {
180 Cleanup(ErrorCode::kError);
181 return;
182 }
183
Alex Deymo20c99202015-07-09 16:14:16 -0700184 if (read_done_) {
Allie Woodeb9e6d82015-04-17 13:55:30 -0700185 // We're done!
186 ErrorCode code = ErrorCode::kSuccess;
187 if (hasher_.Finalize()) {
188 LOG(INFO) << "Hash: " << hasher_.hash();
189 switch (partition_type_) {
190 case PartitionType::kRootfs:
191 if (install_plan_.rootfs_hash != hasher_.raw_hash()) {
192 code = ErrorCode::kNewRootfsVerificationError;
193 LOG(ERROR) << "New rootfs verification failed.";
194 }
195 break;
196 case PartitionType::kKernel:
197 if (install_plan_.kernel_hash != hasher_.raw_hash()) {
198 code = ErrorCode::kNewKernelVerificationError;
199 LOG(ERROR) << "New kernel verification failed.";
200 }
201 break;
202 case PartitionType::kSourceRootfs:
203 install_plan_.source_rootfs_hash = hasher_.raw_hash();
204 break;
205 case PartitionType::kSourceKernel:
206 install_plan_.source_kernel_hash = hasher_.raw_hash();
207 break;
208 }
209 } else {
210 LOG(ERROR) << "Unable to finalize the hash.";
211 code = ErrorCode::kError;
212 }
213 Cleanup(code);
214 }
215}
216
217void FilesystemVerifierAction::DetermineFilesystemSize(int fd) {
218 switch (partition_type_) {
219 case PartitionType::kRootfs:
220 remaining_size_ = install_plan_.rootfs_size;
221 LOG(INFO) << "Filesystem size: " << remaining_size_ << " bytes.";
222 break;
223 case PartitionType::kKernel:
224 remaining_size_ = install_plan_.kernel_size;
225 LOG(INFO) << "Filesystem size: " << remaining_size_ << " bytes.";
226 break;
227 case PartitionType::kSourceRootfs:
228 {
229 int block_count = 0, block_size = 0;
230 if (utils::GetFilesystemSizeFromFD(fd, &block_count, &block_size)) {
231 remaining_size_ = static_cast<int64_t>(block_count) * block_size;
232 LOG(INFO) << "Filesystem size: " << remaining_size_ << " bytes ("
233 << block_count << "x" << block_size << ").";
234 }
235 }
236 break;
237 default:
238 break;
239 }
240 return;
241}
242
243} // namespace chromeos_update_engine