blob: 5ca80b1a46c8474ea71cf1dfdcc79bf4c12a206a [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
Alex Deymo763e7db2015-08-27 21:08:08 -070031#include "update_engine/boot_control_interface.h"
Sen Jiang70a6ab02015-08-28 13:23:27 -070032#include "update_engine/payload_constants.h"
Allie Woodeb9e6d82015-04-17 13:55:30 -070033#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(
Alex Deymoe5e5fe92015-10-05 09:28:19 -070044 const BootControlInterface* boot_control,
45 VerifierMode verifier_mode)
46 : verifier_mode_(verifier_mode),
47 boot_control_(boot_control) {}
Allie Woodeb9e6d82015-04-17 13:55:30 -070048
49void FilesystemVerifierAction::PerformAction() {
50 // Will tell the ActionProcessor we've failed if we return.
51 ScopedActionCompleter abort_action_completer(processor_, this);
52
53 if (!HasInputObject()) {
54 LOG(ERROR) << "FilesystemVerifierAction missing input object.";
55 return;
56 }
57 install_plan_ = GetInputObject();
58
Alex Deymoe5e5fe92015-10-05 09:28:19 -070059 // For delta updates (major version 1) we need to populate the source
60 // partition hash if not pre-populated.
61 if (!install_plan_.is_full_update && install_plan_.partitions.empty() &&
62 verifier_mode_ == VerifierMode::kComputeSourceHash) {
63 LOG(INFO) << "Using legacy partition names.";
64 InstallPlan::Partition part;
65 string part_path;
66
67 part.name = kLegacyPartitionNameRoot;
68 if (!boot_control_->GetPartitionDevice(
69 part.name, install_plan_.source_slot, &part_path))
70 return;
71 int block_count = 0, block_size = 0;
72 if (utils::GetFilesystemSize(part_path, &block_count, &block_size)) {
73 part.source_size = static_cast<int64_t>(block_count) * block_size;
74 LOG(INFO) << "Partition " << part.name << " size: " << part.source_size
75 << " bytes (" << block_count << "x" << block_size << ").";
76 }
77 install_plan_.partitions.push_back(part);
78
79 part.name = kLegacyPartitionNameKernel;
80 if (!boot_control_->GetPartitionDevice(
81 part.name, install_plan_.source_slot, &part_path))
82 return;
83 off_t kernel_part_size = utils::FileSize(part_path);
84 if (kernel_part_size < 0)
85 return;
86 LOG(INFO) << "Partition " << part.name << " size: " << kernel_part_size
87 << " bytes.";
88 part.source_size = kernel_part_size;
89 install_plan_.partitions.push_back(part);
90 }
91
92 if (install_plan_.partitions.empty()) {
93 LOG(INFO) << "No partitions to verify.";
Allie Woodeb9e6d82015-04-17 13:55:30 -070094 if (HasOutputPipe())
95 SetOutputObject(install_plan_);
96 abort_action_completer.set_code(ErrorCode::kSuccess);
97 return;
98 }
99
Alex Deymoe5e5fe92015-10-05 09:28:19 -0700100 StartPartitionHashing();
Allie Woodeb9e6d82015-04-17 13:55:30 -0700101 abort_action_completer.set_should_complete(false);
102}
103
104void FilesystemVerifierAction::TerminateProcessing() {
Alex Deymo20c99202015-07-09 16:14:16 -0700105 cancelled_ = true;
106 Cleanup(ErrorCode::kSuccess); // error code is ignored if canceled_ is true.
Allie Woodeb9e6d82015-04-17 13:55:30 -0700107}
108
109bool FilesystemVerifierAction::IsCleanupPending() const {
Alex Deymob9e8e262015-08-03 20:23:03 -0700110 return src_stream_ != nullptr;
Allie Woodeb9e6d82015-04-17 13:55:30 -0700111}
112
113void FilesystemVerifierAction::Cleanup(ErrorCode code) {
Alex Deymob9e8e262015-08-03 20:23:03 -0700114 src_stream_.reset();
Alex Deymo20c99202015-07-09 16:14:16 -0700115 // This memory is not used anymore.
116 buffer_.clear();
117
Allie Woodeb9e6d82015-04-17 13:55:30 -0700118 if (cancelled_)
119 return;
120 if (code == ErrorCode::kSuccess && HasOutputPipe())
121 SetOutputObject(install_plan_);
122 processor_->ActionComplete(this, code);
123}
124
Alex Deymoe5e5fe92015-10-05 09:28:19 -0700125void FilesystemVerifierAction::StartPartitionHashing() {
126 if (partition_index_ == install_plan_.partitions.size()) {
127 Cleanup(ErrorCode::kSuccess);
128 return;
129 }
130 InstallPlan::Partition& partition =
131 install_plan_.partitions[partition_index_];
132
133 string part_path;
134 switch (verifier_mode_) {
135 case VerifierMode::kComputeSourceHash:
136 boot_control_->GetPartitionDevice(
137 partition.name, install_plan_.source_slot, &part_path);
138 remaining_size_ = partition.source_size;
139 break;
140 case VerifierMode::kVerifyTargetHash:
141 boot_control_->GetPartitionDevice(
142 partition.name, install_plan_.target_slot, &part_path);
143 remaining_size_ = partition.target_size;
144 break;
145 }
146 LOG(INFO) << "Hashing partition " << partition_index_ << " ("
147 << partition.name << ") on device " << part_path;
148 if (part_path.empty())
149 return Cleanup(ErrorCode::kFilesystemVerifierError);
150
151 chromeos::ErrorPtr error;
152 src_stream_ = chromeos::FileStream::Open(
153 base::FilePath(part_path),
154 chromeos::Stream::AccessMode::READ,
155 chromeos::FileStream::Disposition::OPEN_EXISTING,
156 &error);
157
158 if (!src_stream_) {
159 LOG(ERROR) << "Unable to open " << part_path << " for reading";
160 return Cleanup(ErrorCode::kFilesystemVerifierError);
161 }
162
163 buffer_.resize(kReadFileBufferSize);
164 read_done_ = false;
165 hasher_.reset(new OmahaHashCalculator());
166
167 // Start the first read.
168 ScheduleRead();
169}
170
Alex Deymob9e8e262015-08-03 20:23:03 -0700171void FilesystemVerifierAction::ScheduleRead() {
Alex Deymo20c99202015-07-09 16:14:16 -0700172 size_t bytes_to_read = std::min(static_cast<int64_t>(buffer_.size()),
173 remaining_size_);
Alex Deymob9e8e262015-08-03 20:23:03 -0700174 if (!bytes_to_read) {
175 OnReadDoneCallback(0);
Allie Woodeb9e6d82015-04-17 13:55:30 -0700176 return;
177 }
178
Alex Deymob9e8e262015-08-03 20:23:03 -0700179 bool read_async_ok = src_stream_->ReadAsync(
180 buffer_.data(),
181 bytes_to_read,
182 base::Bind(&FilesystemVerifierAction::OnReadDoneCallback,
183 base::Unretained(this)),
184 base::Bind(&FilesystemVerifierAction::OnReadErrorCallback,
185 base::Unretained(this)),
186 nullptr);
187
188 if (!read_async_ok) {
189 LOG(ERROR) << "Unable to schedule an asynchronous read from the stream.";
190 Cleanup(ErrorCode::kError);
Allie Woodeb9e6d82015-04-17 13:55:30 -0700191 }
192}
193
Alex Deymob9e8e262015-08-03 20:23:03 -0700194void FilesystemVerifierAction::OnReadDoneCallback(size_t bytes_read) {
195 if (bytes_read == 0) {
196 read_done_ = true;
197 } else {
198 remaining_size_ -= bytes_read;
199 CHECK(!read_done_);
Alex Deymoe5e5fe92015-10-05 09:28:19 -0700200 if (!hasher_->Update(buffer_.data(), bytes_read)) {
Alex Deymob9e8e262015-08-03 20:23:03 -0700201 LOG(ERROR) << "Unable to update the hash.";
202 Cleanup(ErrorCode::kError);
203 return;
204 }
205 }
206
Alex Deymoe5e5fe92015-10-05 09:28:19 -0700207 // We either terminate the current partition or have more data to read.
208 if (cancelled_)
209 return Cleanup(ErrorCode::kError);
210
211 if (read_done_ || remaining_size_ == 0) {
212 if (remaining_size_ != 0) {
213 LOG(ERROR) << "Failed to read the remaining " << remaining_size_
214 << " bytes from partition "
215 << install_plan_.partitions[partition_index_].name;
216 return Cleanup(ErrorCode::kFilesystemVerifierError);
217 }
218 return FinishPartitionHashing();
219 }
220 ScheduleRead();
Alex Deymob9e8e262015-08-03 20:23:03 -0700221}
222
223void FilesystemVerifierAction::OnReadErrorCallback(
224 const chromeos::Error* error) {
225 // TODO(deymo): Transform the read-error into an specific ErrorCode.
226 LOG(ERROR) << "Asynchronous read failed.";
227 Cleanup(ErrorCode::kError);
228}
229
Alex Deymoe5e5fe92015-10-05 09:28:19 -0700230void FilesystemVerifierAction::FinishPartitionHashing() {
231 if (!hasher_->Finalize()) {
Alex Deymob9e8e262015-08-03 20:23:03 -0700232 LOG(ERROR) << "Unable to finalize the hash.";
Alex Deymoe5e5fe92015-10-05 09:28:19 -0700233 return Cleanup(ErrorCode::kError);
Alex Deymob9e8e262015-08-03 20:23:03 -0700234 }
Alex Deymoe5e5fe92015-10-05 09:28:19 -0700235 InstallPlan::Partition& partition =
236 install_plan_.partitions[partition_index_];
237 LOG(INFO) << "Hash of " << partition.name << ": " << hasher_->hash();
Alex Deymob9e8e262015-08-03 20:23:03 -0700238
Alex Deymoe5e5fe92015-10-05 09:28:19 -0700239 switch (verifier_mode_) {
240 case VerifierMode::kComputeSourceHash:
241 partition.source_hash = hasher_->raw_hash();
Allie Woodeb9e6d82015-04-17 13:55:30 -0700242 break;
Alex Deymoe5e5fe92015-10-05 09:28:19 -0700243 case VerifierMode::kVerifyTargetHash:
244 if (partition.target_hash != hasher_->raw_hash()) {
245 LOG(ERROR) << "New '" << partition.name
246 << "' partition verification failed.";
247 return Cleanup(ErrorCode::kNewRootfsVerificationError);
Allie Woodeb9e6d82015-04-17 13:55:30 -0700248 }
249 break;
Allie Woodeb9e6d82015-04-17 13:55:30 -0700250 }
Alex Deymoe5e5fe92015-10-05 09:28:19 -0700251 // Start hashing the next partition, if any.
252 partition_index_++;
253 hasher_.reset();
254 buffer_.clear();
255 src_stream_->CloseBlocking(nullptr);
256 StartPartitionHashing();
Allie Woodeb9e6d82015-04-17 13:55:30 -0700257}
258
259} // namespace chromeos_update_engine