blob: d3e9fe3eb1ebcc6aa6e06a56d545b6a56ca76b23 [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>
Alex Deymob9e8e262015-08-03 20:23:03 -070017#include <chromeos/streams/file_stream.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
23using std::string;
24
25namespace chromeos_update_engine {
26
27namespace {
Alex Deymo20c99202015-07-09 16:14:16 -070028const off_t kReadFileBufferSize = 128 * 1024;
Allie Woodeb9e6d82015-04-17 13:55:30 -070029} // namespace
30
31FilesystemVerifierAction::FilesystemVerifierAction(
32 SystemState* system_state,
33 PartitionType partition_type)
34 : partition_type_(partition_type),
Allie Woodeb9e6d82015-04-17 13:55:30 -070035 remaining_size_(kint64max),
36 system_state_(system_state) {}
37
38void FilesystemVerifierAction::PerformAction() {
39 // Will tell the ActionProcessor we've failed if we return.
40 ScopedActionCompleter abort_action_completer(processor_, this);
41
42 if (!HasInputObject()) {
43 LOG(ERROR) << "FilesystemVerifierAction missing input object.";
44 return;
45 }
46 install_plan_ = GetInputObject();
47
48 if (partition_type_ == PartitionType::kKernel) {
49 LOG(INFO) << "verifying kernel, marking as unbootable";
50 if (!system_state_->hardware()->MarkKernelUnbootable(
51 install_plan_.kernel_install_path)) {
52 PLOG(ERROR) << "Unable to clear kernel GPT boot flags: " <<
53 install_plan_.kernel_install_path;
54 }
55 }
56
57 if (install_plan_.is_full_update &&
58 (partition_type_ == PartitionType::kSourceRootfs ||
59 partition_type_ == PartitionType::kSourceKernel)) {
60 // No hash verification needed. Done!
61 LOG(INFO) << "filesystem verifying skipped on full update.";
62 if (HasOutputPipe())
63 SetOutputObject(install_plan_);
64 abort_action_completer.set_code(ErrorCode::kSuccess);
65 return;
66 }
67
68 string target_path;
69 switch (partition_type_) {
70 case PartitionType::kRootfs:
71 target_path = install_plan_.install_path;
72 if (target_path.empty()) {
73 utils::GetInstallDev(system_state_->hardware()->BootDevice(),
74 &target_path);
75 }
76 break;
77 case PartitionType::kKernel:
78 target_path = install_plan_.kernel_install_path;
79 if (target_path.empty()) {
80 string rootfs_path;
81 utils::GetInstallDev(system_state_->hardware()->BootDevice(),
82 &rootfs_path);
83 target_path = utils::KernelDeviceOfBootDevice(rootfs_path);
84 }
85 break;
86 case PartitionType::kSourceRootfs:
87 target_path = install_plan_.source_path.empty() ?
88 system_state_->hardware()->BootDevice() :
89 install_plan_.source_path;
90 break;
91 case PartitionType::kSourceKernel:
92 target_path = install_plan_.kernel_source_path.empty() ?
93 utils::KernelDeviceOfBootDevice(
94 system_state_->hardware()->BootDevice()) :
95 install_plan_.kernel_source_path;
96 break;
97 }
98
Alex Deymob9e8e262015-08-03 20:23:03 -070099 chromeos::ErrorPtr error;
100 src_stream_ = chromeos::FileStream::Open(
101 base::FilePath(target_path),
102 chromeos::Stream::AccessMode::READ,
103 chromeos::FileStream::Disposition::OPEN_EXISTING,
104 &error);
105
106 if (!src_stream_) {
107 LOG(ERROR) << "Unable to open " << target_path << " for reading";
Allie Woodeb9e6d82015-04-17 13:55:30 -0700108 return;
109 }
110
Alex Deymob9e8e262015-08-03 20:23:03 -0700111 DetermineFilesystemSize(target_path);
Alex Deymo20c99202015-07-09 16:14:16 -0700112 buffer_.resize(kReadFileBufferSize);
Allie Woodeb9e6d82015-04-17 13:55:30 -0700113
114 // Start the first read.
Alex Deymob9e8e262015-08-03 20:23:03 -0700115 ScheduleRead();
Allie Woodeb9e6d82015-04-17 13:55:30 -0700116
117 abort_action_completer.set_should_complete(false);
118}
119
120void FilesystemVerifierAction::TerminateProcessing() {
Alex Deymo20c99202015-07-09 16:14:16 -0700121 cancelled_ = true;
122 Cleanup(ErrorCode::kSuccess); // error code is ignored if canceled_ is true.
Allie Woodeb9e6d82015-04-17 13:55:30 -0700123}
124
125bool FilesystemVerifierAction::IsCleanupPending() const {
Alex Deymob9e8e262015-08-03 20:23:03 -0700126 return src_stream_ != nullptr;
Allie Woodeb9e6d82015-04-17 13:55:30 -0700127}
128
129void FilesystemVerifierAction::Cleanup(ErrorCode code) {
Alex Deymob9e8e262015-08-03 20:23:03 -0700130 src_stream_.reset();
Alex Deymo20c99202015-07-09 16:14:16 -0700131 // This memory is not used anymore.
132 buffer_.clear();
133
Allie Woodeb9e6d82015-04-17 13:55:30 -0700134 if (cancelled_)
135 return;
136 if (code == ErrorCode::kSuccess && HasOutputPipe())
137 SetOutputObject(install_plan_);
138 processor_->ActionComplete(this, code);
139}
140
Alex Deymob9e8e262015-08-03 20:23:03 -0700141void FilesystemVerifierAction::ScheduleRead() {
Alex Deymo20c99202015-07-09 16:14:16 -0700142 size_t bytes_to_read = std::min(static_cast<int64_t>(buffer_.size()),
143 remaining_size_);
Alex Deymob9e8e262015-08-03 20:23:03 -0700144 if (!bytes_to_read) {
145 OnReadDoneCallback(0);
Allie Woodeb9e6d82015-04-17 13:55:30 -0700146 return;
147 }
148
Alex Deymob9e8e262015-08-03 20:23:03 -0700149 bool read_async_ok = src_stream_->ReadAsync(
150 buffer_.data(),
151 bytes_to_read,
152 base::Bind(&FilesystemVerifierAction::OnReadDoneCallback,
153 base::Unretained(this)),
154 base::Bind(&FilesystemVerifierAction::OnReadErrorCallback,
155 base::Unretained(this)),
156 nullptr);
157
158 if (!read_async_ok) {
159 LOG(ERROR) << "Unable to schedule an asynchronous read from the stream.";
160 Cleanup(ErrorCode::kError);
Allie Woodeb9e6d82015-04-17 13:55:30 -0700161 }
162}
163
Alex Deymob9e8e262015-08-03 20:23:03 -0700164void FilesystemVerifierAction::OnReadDoneCallback(size_t bytes_read) {
165 if (bytes_read == 0) {
166 read_done_ = true;
167 } else {
168 remaining_size_ -= bytes_read;
169 CHECK(!read_done_);
170 if (!hasher_.Update(buffer_.data(), bytes_read)) {
171 LOG(ERROR) << "Unable to update the hash.";
172 Cleanup(ErrorCode::kError);
173 return;
174 }
175 }
176
177 // We either terminate the action or have more data to read.
178 if (!CheckTerminationConditions())
179 ScheduleRead();
180}
181
182void FilesystemVerifierAction::OnReadErrorCallback(
183 const chromeos::Error* error) {
184 // TODO(deymo): Transform the read-error into an specific ErrorCode.
185 LOG(ERROR) << "Asynchronous read failed.";
186 Cleanup(ErrorCode::kError);
187}
188
189bool FilesystemVerifierAction::CheckTerminationConditions() {
190 if (cancelled_) {
191 Cleanup(ErrorCode::kError);
192 return true;
193 }
194
195 if (!read_done_)
196 return false;
197
198 // We're done!
199 ErrorCode code = ErrorCode::kSuccess;
200 if (hasher_.Finalize()) {
201 LOG(INFO) << "Hash: " << hasher_.hash();
202 switch (partition_type_) {
203 case PartitionType::kRootfs:
204 if (install_plan_.rootfs_hash != hasher_.raw_hash()) {
205 code = ErrorCode::kNewRootfsVerificationError;
206 LOG(ERROR) << "New rootfs verification failed.";
207 }
208 break;
209 case PartitionType::kKernel:
210 if (install_plan_.kernel_hash != hasher_.raw_hash()) {
211 code = ErrorCode::kNewKernelVerificationError;
212 LOG(ERROR) << "New kernel verification failed.";
213 }
214 break;
215 case PartitionType::kSourceRootfs:
216 install_plan_.source_rootfs_hash = hasher_.raw_hash();
217 break;
218 case PartitionType::kSourceKernel:
219 install_plan_.source_kernel_hash = hasher_.raw_hash();
220 break;
221 }
222 } else {
223 LOG(ERROR) << "Unable to finalize the hash.";
224 code = ErrorCode::kError;
225 }
226 Cleanup(code);
227 return true;
228}
229
230void FilesystemVerifierAction::DetermineFilesystemSize(
231 const std::string& path) {
Allie Woodeb9e6d82015-04-17 13:55:30 -0700232 switch (partition_type_) {
233 case PartitionType::kRootfs:
234 remaining_size_ = install_plan_.rootfs_size;
235 LOG(INFO) << "Filesystem size: " << remaining_size_ << " bytes.";
236 break;
237 case PartitionType::kKernel:
238 remaining_size_ = install_plan_.kernel_size;
239 LOG(INFO) << "Filesystem size: " << remaining_size_ << " bytes.";
240 break;
241 case PartitionType::kSourceRootfs:
242 {
243 int block_count = 0, block_size = 0;
Alex Deymob9e8e262015-08-03 20:23:03 -0700244 if (utils::GetFilesystemSize(path, &block_count, &block_size)) {
Allie Woodeb9e6d82015-04-17 13:55:30 -0700245 remaining_size_ = static_cast<int64_t>(block_count) * block_size;
246 LOG(INFO) << "Filesystem size: " << remaining_size_ << " bytes ("
247 << block_count << "x" << block_size << ").";
248 }
249 }
250 break;
251 default:
252 break;
253 }
Allie Woodeb9e6d82015-04-17 13:55:30 -0700254}
255
256} // namespace chromeos_update_engine