blob: bfe3da70d43fd3945996a1c616ccd7f968ec6d3c [file] [log] [blame]
Alex Deymoaea4c1c2015-08-19 20:24:43 -07001//
2// Copyright (C) 2012 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//
Allie Woodeb9e6d82015-04-17 13:55:30 -070016
17#include "update_engine/filesystem_verifier_action.h"
18
19#include <errno.h>
20#include <fcntl.h>
21#include <sys/stat.h>
22#include <sys/types.h>
23
24#include <algorithm>
25#include <cstdlib>
26#include <string>
27
Alex Deymo20c99202015-07-09 16:14:16 -070028#include <base/bind.h>
Alex Deymob9e8e262015-08-03 20:23:03 -070029#include <chromeos/streams/file_stream.h>
Allie Woodeb9e6d82015-04-17 13:55:30 -070030
Allie Woodeb9e6d82015-04-17 13:55:30 -070031#include "update_engine/hardware_interface.h"
Allie Woodeb9e6d82015-04-17 13:55:30 -070032#include "update_engine/system_state.h"
33#include "update_engine/utils.h"
34
35using std::string;
36
37namespace chromeos_update_engine {
38
39namespace {
Alex Deymo20c99202015-07-09 16:14:16 -070040const off_t kReadFileBufferSize = 128 * 1024;
Allie Woodeb9e6d82015-04-17 13:55:30 -070041} // namespace
42
43FilesystemVerifierAction::FilesystemVerifierAction(
44 SystemState* system_state,
45 PartitionType partition_type)
46 : partition_type_(partition_type),
Allie Woodeb9e6d82015-04-17 13:55:30 -070047 remaining_size_(kint64max),
48 system_state_(system_state) {}
49
50void FilesystemVerifierAction::PerformAction() {
51 // Will tell the ActionProcessor we've failed if we return.
52 ScopedActionCompleter abort_action_completer(processor_, this);
53
54 if (!HasInputObject()) {
55 LOG(ERROR) << "FilesystemVerifierAction missing input object.";
56 return;
57 }
58 install_plan_ = GetInputObject();
59
60 if (partition_type_ == PartitionType::kKernel) {
61 LOG(INFO) << "verifying kernel, marking as unbootable";
62 if (!system_state_->hardware()->MarkKernelUnbootable(
63 install_plan_.kernel_install_path)) {
64 PLOG(ERROR) << "Unable to clear kernel GPT boot flags: " <<
65 install_plan_.kernel_install_path;
66 }
67 }
68
69 if (install_plan_.is_full_update &&
70 (partition_type_ == PartitionType::kSourceRootfs ||
71 partition_type_ == PartitionType::kSourceKernel)) {
72 // No hash verification needed. Done!
73 LOG(INFO) << "filesystem verifying skipped on full update.";
74 if (HasOutputPipe())
75 SetOutputObject(install_plan_);
76 abort_action_completer.set_code(ErrorCode::kSuccess);
77 return;
78 }
79
80 string target_path;
81 switch (partition_type_) {
82 case PartitionType::kRootfs:
83 target_path = install_plan_.install_path;
84 if (target_path.empty()) {
85 utils::GetInstallDev(system_state_->hardware()->BootDevice(),
86 &target_path);
87 }
88 break;
89 case PartitionType::kKernel:
90 target_path = install_plan_.kernel_install_path;
91 if (target_path.empty()) {
92 string rootfs_path;
93 utils::GetInstallDev(system_state_->hardware()->BootDevice(),
94 &rootfs_path);
95 target_path = utils::KernelDeviceOfBootDevice(rootfs_path);
96 }
97 break;
98 case PartitionType::kSourceRootfs:
99 target_path = install_plan_.source_path.empty() ?
100 system_state_->hardware()->BootDevice() :
101 install_plan_.source_path;
102 break;
103 case PartitionType::kSourceKernel:
104 target_path = install_plan_.kernel_source_path.empty() ?
105 utils::KernelDeviceOfBootDevice(
106 system_state_->hardware()->BootDevice()) :
107 install_plan_.kernel_source_path;
108 break;
109 }
110
Alex Deymob9e8e262015-08-03 20:23:03 -0700111 chromeos::ErrorPtr error;
112 src_stream_ = chromeos::FileStream::Open(
113 base::FilePath(target_path),
114 chromeos::Stream::AccessMode::READ,
115 chromeos::FileStream::Disposition::OPEN_EXISTING,
116 &error);
117
118 if (!src_stream_) {
119 LOG(ERROR) << "Unable to open " << target_path << " for reading";
Allie Woodeb9e6d82015-04-17 13:55:30 -0700120 return;
121 }
122
Alex Deymob9e8e262015-08-03 20:23:03 -0700123 DetermineFilesystemSize(target_path);
Alex Deymo20c99202015-07-09 16:14:16 -0700124 buffer_.resize(kReadFileBufferSize);
Allie Woodeb9e6d82015-04-17 13:55:30 -0700125
126 // Start the first read.
Alex Deymob9e8e262015-08-03 20:23:03 -0700127 ScheduleRead();
Allie Woodeb9e6d82015-04-17 13:55:30 -0700128
129 abort_action_completer.set_should_complete(false);
130}
131
132void FilesystemVerifierAction::TerminateProcessing() {
Alex Deymo20c99202015-07-09 16:14:16 -0700133 cancelled_ = true;
134 Cleanup(ErrorCode::kSuccess); // error code is ignored if canceled_ is true.
Allie Woodeb9e6d82015-04-17 13:55:30 -0700135}
136
137bool FilesystemVerifierAction::IsCleanupPending() const {
Alex Deymob9e8e262015-08-03 20:23:03 -0700138 return src_stream_ != nullptr;
Allie Woodeb9e6d82015-04-17 13:55:30 -0700139}
140
141void FilesystemVerifierAction::Cleanup(ErrorCode code) {
Alex Deymob9e8e262015-08-03 20:23:03 -0700142 src_stream_.reset();
Alex Deymo20c99202015-07-09 16:14:16 -0700143 // This memory is not used anymore.
144 buffer_.clear();
145
Allie Woodeb9e6d82015-04-17 13:55:30 -0700146 if (cancelled_)
147 return;
148 if (code == ErrorCode::kSuccess && HasOutputPipe())
149 SetOutputObject(install_plan_);
150 processor_->ActionComplete(this, code);
151}
152
Alex Deymob9e8e262015-08-03 20:23:03 -0700153void FilesystemVerifierAction::ScheduleRead() {
Alex Deymo20c99202015-07-09 16:14:16 -0700154 size_t bytes_to_read = std::min(static_cast<int64_t>(buffer_.size()),
155 remaining_size_);
Alex Deymob9e8e262015-08-03 20:23:03 -0700156 if (!bytes_to_read) {
157 OnReadDoneCallback(0);
Allie Woodeb9e6d82015-04-17 13:55:30 -0700158 return;
159 }
160
Alex Deymob9e8e262015-08-03 20:23:03 -0700161 bool read_async_ok = src_stream_->ReadAsync(
162 buffer_.data(),
163 bytes_to_read,
164 base::Bind(&FilesystemVerifierAction::OnReadDoneCallback,
165 base::Unretained(this)),
166 base::Bind(&FilesystemVerifierAction::OnReadErrorCallback,
167 base::Unretained(this)),
168 nullptr);
169
170 if (!read_async_ok) {
171 LOG(ERROR) << "Unable to schedule an asynchronous read from the stream.";
172 Cleanup(ErrorCode::kError);
Allie Woodeb9e6d82015-04-17 13:55:30 -0700173 }
174}
175
Alex Deymob9e8e262015-08-03 20:23:03 -0700176void FilesystemVerifierAction::OnReadDoneCallback(size_t bytes_read) {
177 if (bytes_read == 0) {
178 read_done_ = true;
179 } else {
180 remaining_size_ -= bytes_read;
181 CHECK(!read_done_);
182 if (!hasher_.Update(buffer_.data(), bytes_read)) {
183 LOG(ERROR) << "Unable to update the hash.";
184 Cleanup(ErrorCode::kError);
185 return;
186 }
187 }
188
189 // We either terminate the action or have more data to read.
190 if (!CheckTerminationConditions())
191 ScheduleRead();
192}
193
194void FilesystemVerifierAction::OnReadErrorCallback(
195 const chromeos::Error* error) {
196 // TODO(deymo): Transform the read-error into an specific ErrorCode.
197 LOG(ERROR) << "Asynchronous read failed.";
198 Cleanup(ErrorCode::kError);
199}
200
201bool FilesystemVerifierAction::CheckTerminationConditions() {
202 if (cancelled_) {
203 Cleanup(ErrorCode::kError);
204 return true;
205 }
206
207 if (!read_done_)
208 return false;
209
210 // We're done!
211 ErrorCode code = ErrorCode::kSuccess;
212 if (hasher_.Finalize()) {
213 LOG(INFO) << "Hash: " << hasher_.hash();
214 switch (partition_type_) {
215 case PartitionType::kRootfs:
216 if (install_plan_.rootfs_hash != hasher_.raw_hash()) {
217 code = ErrorCode::kNewRootfsVerificationError;
218 LOG(ERROR) << "New rootfs verification failed.";
219 }
220 break;
221 case PartitionType::kKernel:
222 if (install_plan_.kernel_hash != hasher_.raw_hash()) {
223 code = ErrorCode::kNewKernelVerificationError;
224 LOG(ERROR) << "New kernel verification failed.";
225 }
226 break;
227 case PartitionType::kSourceRootfs:
228 install_plan_.source_rootfs_hash = hasher_.raw_hash();
229 break;
230 case PartitionType::kSourceKernel:
231 install_plan_.source_kernel_hash = hasher_.raw_hash();
232 break;
233 }
234 } else {
235 LOG(ERROR) << "Unable to finalize the hash.";
236 code = ErrorCode::kError;
237 }
238 Cleanup(code);
239 return true;
240}
241
242void FilesystemVerifierAction::DetermineFilesystemSize(
243 const std::string& path) {
Allie Woodeb9e6d82015-04-17 13:55:30 -0700244 switch (partition_type_) {
245 case PartitionType::kRootfs:
246 remaining_size_ = install_plan_.rootfs_size;
247 LOG(INFO) << "Filesystem size: " << remaining_size_ << " bytes.";
248 break;
249 case PartitionType::kKernel:
250 remaining_size_ = install_plan_.kernel_size;
251 LOG(INFO) << "Filesystem size: " << remaining_size_ << " bytes.";
252 break;
253 case PartitionType::kSourceRootfs:
254 {
255 int block_count = 0, block_size = 0;
Alex Deymob9e8e262015-08-03 20:23:03 -0700256 if (utils::GetFilesystemSize(path, &block_count, &block_size)) {
Allie Woodeb9e6d82015-04-17 13:55:30 -0700257 remaining_size_ = static_cast<int64_t>(block_count) * block_size;
258 LOG(INFO) << "Filesystem size: " << remaining_size_ << " bytes ("
259 << block_count << "x" << block_size << ").";
260 }
261 }
262 break;
263 default:
264 break;
265 }
Allie Woodeb9e6d82015-04-17 13:55:30 -0700266}
267
268} // namespace chromeos_update_engine