blob: 88107639fd3e9b0999d4099380972d3c35635f60 [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
16#include <gio/gio.h>
17#include <gio/gunixinputstream.h>
18#include <glib.h>
19
20#include "update_engine/glib_utils.h"
21#include "update_engine/hardware_interface.h"
22#include "update_engine/subprocess.h"
23#include "update_engine/system_state.h"
24#include "update_engine/utils.h"
25
26using std::string;
27
28namespace chromeos_update_engine {
29
30namespace {
31const off_t kCopyFileBufferSize = 128 * 1024;
32} // namespace
33
34FilesystemVerifierAction::FilesystemVerifierAction(
35 SystemState* system_state,
36 PartitionType partition_type)
37 : partition_type_(partition_type),
38 src_stream_(nullptr),
39 canceller_(nullptr),
40 read_done_(false),
41 failed_(false),
42 cancelled_(false),
43 remaining_size_(kint64max),
44 system_state_(system_state) {}
45
46void FilesystemVerifierAction::PerformAction() {
47 // Will tell the ActionProcessor we've failed if we return.
48 ScopedActionCompleter abort_action_completer(processor_, this);
49
50 if (!HasInputObject()) {
51 LOG(ERROR) << "FilesystemVerifierAction missing input object.";
52 return;
53 }
54 install_plan_ = GetInputObject();
55
56 if (partition_type_ == PartitionType::kKernel) {
57 LOG(INFO) << "verifying kernel, marking as unbootable";
58 if (!system_state_->hardware()->MarkKernelUnbootable(
59 install_plan_.kernel_install_path)) {
60 PLOG(ERROR) << "Unable to clear kernel GPT boot flags: " <<
61 install_plan_.kernel_install_path;
62 }
63 }
64
65 if (install_plan_.is_full_update &&
66 (partition_type_ == PartitionType::kSourceRootfs ||
67 partition_type_ == PartitionType::kSourceKernel)) {
68 // No hash verification needed. Done!
69 LOG(INFO) << "filesystem verifying skipped on full update.";
70 if (HasOutputPipe())
71 SetOutputObject(install_plan_);
72 abort_action_completer.set_code(ErrorCode::kSuccess);
73 return;
74 }
75
76 string target_path;
77 switch (partition_type_) {
78 case PartitionType::kRootfs:
79 target_path = install_plan_.install_path;
80 if (target_path.empty()) {
81 utils::GetInstallDev(system_state_->hardware()->BootDevice(),
82 &target_path);
83 }
84 break;
85 case PartitionType::kKernel:
86 target_path = install_plan_.kernel_install_path;
87 if (target_path.empty()) {
88 string rootfs_path;
89 utils::GetInstallDev(system_state_->hardware()->BootDevice(),
90 &rootfs_path);
91 target_path = utils::KernelDeviceOfBootDevice(rootfs_path);
92 }
93 break;
94 case PartitionType::kSourceRootfs:
95 target_path = install_plan_.source_path.empty() ?
96 system_state_->hardware()->BootDevice() :
97 install_plan_.source_path;
98 break;
99 case PartitionType::kSourceKernel:
100 target_path = install_plan_.kernel_source_path.empty() ?
101 utils::KernelDeviceOfBootDevice(
102 system_state_->hardware()->BootDevice()) :
103 install_plan_.kernel_source_path;
104 break;
105 }
106
107 int src_fd = open(target_path.c_str(), O_RDONLY);
108 if (src_fd < 0) {
109 PLOG(ERROR) << "Unable to open " << target_path << " for reading:";
110 return;
111 }
112
113 DetermineFilesystemSize(src_fd);
114 src_stream_ = g_unix_input_stream_new(src_fd, TRUE);
115
116 buffer_.resize(kCopyFileBufferSize);
117 canceller_ = g_cancellable_new();
118
119 // Start the first read.
120 SpawnAsyncActions();
121
122 abort_action_completer.set_should_complete(false);
123}
124
125void FilesystemVerifierAction::TerminateProcessing() {
126 if (canceller_) {
127 g_cancellable_cancel(canceller_);
128 }
129}
130
131bool FilesystemVerifierAction::IsCleanupPending() const {
132 return (src_stream_ != nullptr);
133}
134
135void FilesystemVerifierAction::Cleanup(ErrorCode code) {
136 g_object_unref(canceller_);
137 canceller_ = nullptr;
138 g_object_unref(src_stream_);
139 src_stream_ = nullptr;
140 if (cancelled_)
141 return;
142 if (code == ErrorCode::kSuccess && HasOutputPipe())
143 SetOutputObject(install_plan_);
144 processor_->ActionComplete(this, code);
145}
146
147void FilesystemVerifierAction::AsyncReadReadyCallback(GObject *source_object,
148 GAsyncResult *res) {
149 GError* error = nullptr;
150 CHECK(canceller_);
151 cancelled_ = g_cancellable_is_cancelled(canceller_) == TRUE;
152
153 ssize_t bytes_read = g_input_stream_read_finish(src_stream_, res, &error);
154
155 if (bytes_read < 0) {
156 LOG(ERROR) << "Read failed: " << utils::GetAndFreeGError(&error);
157 failed_ = true;
158 } else if (bytes_read == 0) {
159 read_done_ = true;
160 } else {
161 remaining_size_ -= bytes_read;
162 }
163
164 if (bytes_read > 0) {
165 // If read_done_ is set, SpawnAsyncActions may finalize the hash so the hash
166 // update below would happen too late.
167 CHECK(!read_done_);
168 if (!hasher_.Update(buffer_.data(), bytes_read)) {
169 LOG(ERROR) << "Unable to update the hash.";
170 failed_ = true;
171 }
172 }
173 SpawnAsyncActions();
174}
175
176void FilesystemVerifierAction::StaticAsyncReadReadyCallback(
177 GObject *source_object,
178 GAsyncResult *res,
179 gpointer user_data) {
180 reinterpret_cast<FilesystemVerifierAction*>(user_data)->
181 AsyncReadReadyCallback(source_object, res);
182}
183
184void FilesystemVerifierAction::SpawnAsyncActions() {
185 if (failed_ || cancelled_) {
186 Cleanup(ErrorCode::kError);
187 return;
188 }
189
190 if (!read_done_) {
191 int64_t bytes_to_read = std::min(static_cast<int64_t>(buffer_.size()),
192 remaining_size_);
193 g_input_stream_read_async(
194 src_stream_,
195 buffer_.data(),
196 bytes_to_read,
197 G_PRIORITY_DEFAULT,
198 canceller_,
199 &FilesystemVerifierAction::StaticAsyncReadReadyCallback,
200 this);
201 } else {
202 // We're done!
203 ErrorCode code = ErrorCode::kSuccess;
204 if (hasher_.Finalize()) {
205 LOG(INFO) << "Hash: " << hasher_.hash();
206 switch (partition_type_) {
207 case PartitionType::kRootfs:
208 if (install_plan_.rootfs_hash != hasher_.raw_hash()) {
209 code = ErrorCode::kNewRootfsVerificationError;
210 LOG(ERROR) << "New rootfs verification failed.";
211 }
212 break;
213 case PartitionType::kKernel:
214 if (install_plan_.kernel_hash != hasher_.raw_hash()) {
215 code = ErrorCode::kNewKernelVerificationError;
216 LOG(ERROR) << "New kernel verification failed.";
217 }
218 break;
219 case PartitionType::kSourceRootfs:
220 install_plan_.source_rootfs_hash = hasher_.raw_hash();
221 break;
222 case PartitionType::kSourceKernel:
223 install_plan_.source_kernel_hash = hasher_.raw_hash();
224 break;
225 }
226 } else {
227 LOG(ERROR) << "Unable to finalize the hash.";
228 code = ErrorCode::kError;
229 }
230 Cleanup(code);
231 }
232}
233
234void FilesystemVerifierAction::DetermineFilesystemSize(int fd) {
235 switch (partition_type_) {
236 case PartitionType::kRootfs:
237 remaining_size_ = install_plan_.rootfs_size;
238 LOG(INFO) << "Filesystem size: " << remaining_size_ << " bytes.";
239 break;
240 case PartitionType::kKernel:
241 remaining_size_ = install_plan_.kernel_size;
242 LOG(INFO) << "Filesystem size: " << remaining_size_ << " bytes.";
243 break;
244 case PartitionType::kSourceRootfs:
245 {
246 int block_count = 0, block_size = 0;
247 if (utils::GetFilesystemSizeFromFD(fd, &block_count, &block_size)) {
248 remaining_size_ = static_cast<int64_t>(block_count) * block_size;
249 LOG(INFO) << "Filesystem size: " << remaining_size_ << " bytes ("
250 << block_count << "x" << block_size << ").";
251 }
252 }
253 break;
254 default:
255 break;
256 }
257 return;
258}
259
260} // namespace chromeos_update_engine