blob: a8f56e993685a9091263252e8c78e0ea8cfdeea1 [file] [log] [blame]
Gilad Arnold6dbbd392012-07-10 16:19:11 -07001// Copyright (c) 2012 The Chromium OS Authors. All rights reserved.
adlr@google.com3defe6a2009-12-04 20:57:17 +00002// 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_copier_action.h"
Darin Petkov9b230572010-10-08 10:20:09 -07006
adlr@google.com3defe6a2009-12-04 20:57:17 +00007#include <errno.h>
8#include <fcntl.h>
Alex Deymo161c4a12014-05-16 15:56:21 -07009#include <sys/stat.h>
10#include <sys/types.h>
Darin Petkov9b230572010-10-08 10:20:09 -070011
adlr@google.com3defe6a2009-12-04 20:57:17 +000012#include <algorithm>
Darin Petkov9b230572010-10-08 10:20:09 -070013#include <cstdlib>
Andrew de los Reyes4fe15d02009-12-10 19:01:36 -080014#include <map>
adlr@google.com3defe6a2009-12-04 20:57:17 +000015#include <string>
16#include <vector>
Darin Petkov9b230572010-10-08 10:20:09 -070017
Andrew de los Reyesc7020782010-04-28 10:46:04 -070018#include <gio/gio.h>
19#include <gio/gunixinputstream.h>
20#include <gio/gunixoutputstream.h>
21#include <glib.h>
Darin Petkov9b230572010-10-08 10:20:09 -070022
Alex Deymo44666f92014-07-22 20:29:24 -070023#include "update_engine/glib_utils.h"
Alex Deymo42432912013-07-12 20:21:15 -070024#include "update_engine/hardware_interface.h"
adlr@google.com3defe6a2009-12-04 20:57:17 +000025#include "update_engine/subprocess.h"
Alex Deymo42432912013-07-12 20:21:15 -070026#include "update_engine/system_state.h"
adlr@google.com3defe6a2009-12-04 20:57:17 +000027#include "update_engine/utils.h"
28
Andrew de los Reyes4fe15d02009-12-10 19:01:36 -080029using std::map;
adlr@google.com3defe6a2009-12-04 20:57:17 +000030using std::min;
31using std::string;
32using std::vector;
33
34namespace chromeos_update_engine {
35
36namespace {
Darin Petkov3aefa862010-12-07 14:45:00 -080037const off_t kCopyFileBufferSize = 128 * 1024;
Alex Vakulenkod2779df2014-06-16 13:19:00 -070038} // namespace
adlr@google.com3defe6a2009-12-04 20:57:17 +000039
Darin Petkovc2e4a7d2010-12-02 14:47:06 -080040FilesystemCopierAction::FilesystemCopierAction(
Alex Deymo42432912013-07-12 20:21:15 -070041 SystemState* system_state,
Gilad Arnold581c2ea2012-07-19 12:33:49 -070042 bool copying_kernel_install_path,
43 bool verify_hash)
Darin Petkovc2e4a7d2010-12-02 14:47:06 -080044 : copying_kernel_install_path_(copying_kernel_install_path),
Darin Petkov3aefa862010-12-07 14:45:00 -080045 verify_hash_(verify_hash),
Alex Vakulenko88b591f2014-08-28 16:48:57 -070046 src_stream_(nullptr),
47 dst_stream_(nullptr),
Darin Petkovc2e4a7d2010-12-02 14:47:06 -080048 read_done_(false),
49 failed_(false),
50 cancelled_(false),
Alex Deymo42432912013-07-12 20:21:15 -070051 filesystem_size_(kint64max),
52 system_state_(system_state) {
Darin Petkovc2e4a7d2010-12-02 14:47:06 -080053 // A lot of code works on the implicit assumption that processing is done on
54 // exactly 2 ping-pong buffers.
55 COMPILE_ASSERT(arraysize(buffer_) == 2 &&
56 arraysize(buffer_state_) == 2 &&
57 arraysize(buffer_valid_size_) == 2 &&
58 arraysize(canceller_) == 2,
59 ping_pong_buffers_not_two);
60 for (int i = 0; i < 2; ++i) {
61 buffer_state_[i] = kBufferStateEmpty;
62 buffer_valid_size_[i] = 0;
Alex Vakulenko88b591f2014-08-28 16:48:57 -070063 canceller_[i] = nullptr;
Darin Petkovc2e4a7d2010-12-02 14:47:06 -080064 }
65}
66
adlr@google.com3defe6a2009-12-04 20:57:17 +000067void FilesystemCopierAction::PerformAction() {
Andrew de los Reyesc7020782010-04-28 10:46:04 -070068 // Will tell the ActionProcessor we've failed if we return.
69 ScopedActionCompleter abort_action_completer(processor_, this);
70
adlr@google.com3defe6a2009-12-04 20:57:17 +000071 if (!HasInputObject()) {
Andrew de los Reyesc7020782010-04-28 10:46:04 -070072 LOG(ERROR) << "FilesystemCopierAction missing input object.";
adlr@google.com3defe6a2009-12-04 20:57:17 +000073 return;
74 }
75 install_plan_ = GetInputObject();
Don Garrett83692e42013-11-08 10:11:30 -080076
77 // No need to copy on a resume.
78 if (!verify_hash_ && install_plan_.is_resume) {
Darin Petkov3aefa862010-12-07 14:45:00 -080079 // No copy or hash verification needed. Done!
Don Garrett83692e42013-11-08 10:11:30 -080080 LOG(INFO) << "filesystem copying skipped on resumed update.";
81 if (HasOutputPipe())
82 SetOutputObject(install_plan_);
Gilad Arnoldd1c4d2d2014-06-05 14:07:53 -070083 abort_action_completer.set_code(ErrorCode::kSuccess);
Don Garrett83692e42013-11-08 10:11:30 -080084 return;
85 }
86
87 if (copying_kernel_install_path_) {
88 if (!system_state_->hardware()->MarkKernelUnbootable(
89 install_plan_.kernel_install_path)) {
90 PLOG(ERROR) << "Unable to clear kernel GPT boot flags: " <<
91 install_plan_.kernel_install_path;
92 }
93 }
94
95 if (!verify_hash_ && install_plan_.is_full_update) {
96 // No copy or hash verification needed. Done!
97 LOG(INFO) << "filesystem copying skipped on full update.";
Andrew de los Reyesf98bff82010-05-06 13:33:25 -070098 if (HasOutputPipe())
99 SetOutputObject(install_plan_);
Gilad Arnoldd1c4d2d2014-06-05 14:07:53 -0700100 abort_action_completer.set_code(ErrorCode::kSuccess);
adlr@google.com3defe6a2009-12-04 20:57:17 +0000101 return;
102 }
103
Darin Petkov3aefa862010-12-07 14:45:00 -0800104 const string destination = copying_kernel_install_path_ ?
105 install_plan_.kernel_install_path :
106 install_plan_.install_path;
107 string source = verify_hash_ ? destination : copy_source_;
Andrew de los Reyesf9185172010-05-03 11:07:05 -0700108 if (source.empty()) {
109 source = copying_kernel_install_path_ ?
J. Richard Barnette30842932013-10-28 15:04:23 -0700110 utils::KernelDeviceOfBootDevice(
Alex Deymo42432912013-07-12 20:21:15 -0700111 system_state_->hardware()->BootDevice()) :
112 system_state_->hardware()->BootDevice();
Andrew de los Reyesf9185172010-05-03 11:07:05 -0700113 }
Andrew de los Reyesc7020782010-04-28 10:46:04 -0700114 int src_fd = open(source.c_str(), O_RDONLY);
115 if (src_fd < 0) {
116 PLOG(ERROR) << "Unable to open " << source << " for reading:";
117 return;
118 }
Darin Petkov3aefa862010-12-07 14:45:00 -0800119
120 if (!verify_hash_) {
121 int dst_fd = open(destination.c_str(),
122 O_WRONLY | O_TRUNC | O_CREAT,
Andrew de los Reyesc7020782010-04-28 10:46:04 -0700123 0644);
Darin Petkov3aefa862010-12-07 14:45:00 -0800124 if (dst_fd < 0) {
125 close(src_fd);
126 PLOG(ERROR) << "Unable to open " << install_plan_.install_path
127 << " for writing:";
128 return;
129 }
130
131 dst_stream_ = g_unix_output_stream_new(dst_fd, TRUE);
adlr@google.com3defe6a2009-12-04 20:57:17 +0000132 }
133
Darin Petkov698d0412010-10-13 10:59:44 -0700134 DetermineFilesystemSize(src_fd);
Andrew de los Reyesc7020782010-04-28 10:46:04 -0700135 src_stream_ = g_unix_input_stream_new(src_fd, TRUE);
Andrew de los Reyesc7020782010-04-28 10:46:04 -0700136
Darin Petkovc2e4a7d2010-12-02 14:47:06 -0800137 for (int i = 0; i < 2; i++) {
138 buffer_[i].resize(kCopyFileBufferSize);
139 canceller_[i] = g_cancellable_new();
140 }
Andrew de los Reyesc7020782010-04-28 10:46:04 -0700141
Darin Petkovc2e4a7d2010-12-02 14:47:06 -0800142 // Start the first read.
143 SpawnAsyncActions();
Andrew de los Reyesc7020782010-04-28 10:46:04 -0700144
145 abort_action_completer.set_should_complete(false);
adlr@google.com3defe6a2009-12-04 20:57:17 +0000146}
147
148void FilesystemCopierAction::TerminateProcessing() {
Darin Petkovc2e4a7d2010-12-02 14:47:06 -0800149 for (int i = 0; i < 2; i++) {
150 if (canceller_[i]) {
151 g_cancellable_cancel(canceller_[i]);
152 }
adlr@google.com3defe6a2009-12-04 20:57:17 +0000153 }
154}
155
Ben Chan2add7d72012-10-08 19:28:37 -0700156bool FilesystemCopierAction::IsCleanupPending() const {
Alex Vakulenko88b591f2014-08-28 16:48:57 -0700157 return (src_stream_ != nullptr);
Ben Chan2add7d72012-10-08 19:28:37 -0700158}
159
David Zeuthena99981f2013-04-29 13:42:47 -0700160void FilesystemCopierAction::Cleanup(ErrorCode code) {
Darin Petkovc2e4a7d2010-12-02 14:47:06 -0800161 for (int i = 0; i < 2; i++) {
162 g_object_unref(canceller_[i]);
Alex Vakulenko88b591f2014-08-28 16:48:57 -0700163 canceller_[i] = nullptr;
Darin Petkovc2e4a7d2010-12-02 14:47:06 -0800164 }
Andrew de los Reyesc7020782010-04-28 10:46:04 -0700165 g_object_unref(src_stream_);
Alex Vakulenko88b591f2014-08-28 16:48:57 -0700166 src_stream_ = nullptr;
Darin Petkov3aefa862010-12-07 14:45:00 -0800167 if (dst_stream_) {
168 g_object_unref(dst_stream_);
Alex Vakulenko88b591f2014-08-28 16:48:57 -0700169 dst_stream_ = nullptr;
Darin Petkov3aefa862010-12-07 14:45:00 -0800170 }
Darin Petkovc2e4a7d2010-12-02 14:47:06 -0800171 if (cancelled_)
Andrew de los Reyesc7020782010-04-28 10:46:04 -0700172 return;
Gilad Arnoldd1c4d2d2014-06-05 14:07:53 -0700173 if (code == ErrorCode::kSuccess && HasOutputPipe())
adlr@google.com3defe6a2009-12-04 20:57:17 +0000174 SetOutputObject(install_plan_);
Darin Petkov3aefa862010-12-07 14:45:00 -0800175 processor_->ActionComplete(this, code);
adlr@google.com3defe6a2009-12-04 20:57:17 +0000176}
177
Darin Petkovc2e4a7d2010-12-02 14:47:06 -0800178void FilesystemCopierAction::AsyncReadReadyCallback(GObject *source_object,
179 GAsyncResult *res) {
180 int index = buffer_state_[0] == kBufferStateReading ? 0 : 1;
181 CHECK(buffer_state_[index] == kBufferStateReading);
182
Alex Vakulenko88b591f2014-08-28 16:48:57 -0700183 GError* error = nullptr;
Darin Petkovc2e4a7d2010-12-02 14:47:06 -0800184 CHECK(canceller_[index]);
185 cancelled_ = g_cancellable_is_cancelled(canceller_[index]) == TRUE;
adlr@google.com3defe6a2009-12-04 20:57:17 +0000186
Darin Petkovc2e4a7d2010-12-02 14:47:06 -0800187 ssize_t bytes_read = g_input_stream_read_finish(src_stream_, res, &error);
188 if (bytes_read < 0) {
Darin Petkova0b9e772011-10-06 05:05:56 -0700189 LOG(ERROR) << "Read failed: " << utils::GetAndFreeGError(&error);
Darin Petkovc2e4a7d2010-12-02 14:47:06 -0800190 failed_ = true;
191 buffer_state_[index] = kBufferStateEmpty;
192 } else if (bytes_read == 0) {
193 read_done_ = true;
194 buffer_state_[index] = kBufferStateEmpty;
195 } else {
196 buffer_valid_size_[index] = bytes_read;
197 buffer_state_[index] = kBufferStateFull;
Darin Petkovc2e4a7d2010-12-02 14:47:06 -0800198 filesystem_size_ -= bytes_read;
199 }
Darin Petkovc2e4a7d2010-12-02 14:47:06 -0800200 SpawnAsyncActions();
201
202 if (bytes_read > 0) {
Darin Petkov3aefa862010-12-07 14:45:00 -0800203 // If read_done_ is set, SpawnAsyncActions may finalize the hash so the hash
204 // update below would happen too late.
205 CHECK(!read_done_);
Darin Petkovc2e4a7d2010-12-02 14:47:06 -0800206 if (!hasher_.Update(buffer_[index].data(), bytes_read)) {
207 LOG(ERROR) << "Unable to update the hash.";
208 failed_ = true;
adlr@google.com3defe6a2009-12-04 20:57:17 +0000209 }
Darin Petkov3aefa862010-12-07 14:45:00 -0800210 if (verify_hash_) {
211 buffer_state_[index] = kBufferStateEmpty;
212 }
Darin Petkovc2e4a7d2010-12-02 14:47:06 -0800213 }
214}
Andrew de los Reyesc7020782010-04-28 10:46:04 -0700215
Darin Petkovc2e4a7d2010-12-02 14:47:06 -0800216void FilesystemCopierAction::StaticAsyncReadReadyCallback(
217 GObject *source_object,
218 GAsyncResult *res,
219 gpointer user_data) {
220 reinterpret_cast<FilesystemCopierAction*>(user_data)->
221 AsyncReadReadyCallback(source_object, res);
222}
223
224void FilesystemCopierAction::AsyncWriteReadyCallback(GObject *source_object,
225 GAsyncResult *res) {
226 int index = buffer_state_[0] == kBufferStateWriting ? 0 : 1;
227 CHECK(buffer_state_[index] == kBufferStateWriting);
Gilad Arnold581c2ea2012-07-19 12:33:49 -0700228 buffer_state_[index] = kBufferStateEmpty;
Darin Petkovc2e4a7d2010-12-02 14:47:06 -0800229
Alex Vakulenko88b591f2014-08-28 16:48:57 -0700230 GError* error = nullptr;
Darin Petkovc2e4a7d2010-12-02 14:47:06 -0800231 CHECK(canceller_[index]);
232 cancelled_ = g_cancellable_is_cancelled(canceller_[index]) == TRUE;
233
234 ssize_t bytes_written = g_output_stream_write_finish(dst_stream_,
235 res,
236 &error);
Gilad Arnold6dbbd392012-07-10 16:19:11 -0700237
Darin Petkovc2e4a7d2010-12-02 14:47:06 -0800238 if (bytes_written < static_cast<ssize_t>(buffer_valid_size_[index])) {
239 if (bytes_written < 0) {
Gilad Arnold6dbbd392012-07-10 16:19:11 -0700240 LOG(ERROR) << "Write error: " << utils::GetAndFreeGError(&error);
Darin Petkovc2e4a7d2010-12-02 14:47:06 -0800241 } else {
Gilad Arnold6dbbd392012-07-10 16:19:11 -0700242 LOG(ERROR) << "Wrote too few bytes: " << bytes_written
Gilad Arnold581c2ea2012-07-19 12:33:49 -0700243 << " < " << buffer_valid_size_[index];
Darin Petkovc2e4a7d2010-12-02 14:47:06 -0800244 }
Gilad Arnold581c2ea2012-07-19 12:33:49 -0700245 failed_ = true;
Darin Petkovc2e4a7d2010-12-02 14:47:06 -0800246 }
247
248 SpawnAsyncActions();
249}
250
251void FilesystemCopierAction::StaticAsyncWriteReadyCallback(
252 GObject *source_object,
253 GAsyncResult *res,
254 gpointer user_data) {
255 reinterpret_cast<FilesystemCopierAction*>(user_data)->
256 AsyncWriteReadyCallback(source_object, res);
257}
258
259void FilesystemCopierAction::SpawnAsyncActions() {
260 bool reading = false;
261 bool writing = false;
262 for (int i = 0; i < 2; i++) {
263 if (buffer_state_[i] == kBufferStateReading) {
264 reading = true;
265 }
266 if (buffer_state_[i] == kBufferStateWriting) {
267 writing = true;
268 }
269 }
270 if (failed_ || cancelled_) {
271 if (!reading && !writing) {
Gilad Arnoldd1c4d2d2014-06-05 14:07:53 -0700272 Cleanup(ErrorCode::kError);
Darin Petkovc2e4a7d2010-12-02 14:47:06 -0800273 }
274 return;
275 }
276 for (int i = 0; i < 2; i++) {
277 if (!reading && !read_done_ && buffer_state_[i] == kBufferStateEmpty) {
Darin Petkov3aefa862010-12-07 14:45:00 -0800278 int64_t bytes_to_read = std::min(static_cast<int64_t>(buffer_[0].size()),
279 filesystem_size_);
Darin Petkovc2e4a7d2010-12-02 14:47:06 -0800280 g_input_stream_read_async(
281 src_stream_,
282 buffer_[i].data(),
Darin Petkov3aefa862010-12-07 14:45:00 -0800283 bytes_to_read,
Darin Petkovc2e4a7d2010-12-02 14:47:06 -0800284 G_PRIORITY_DEFAULT,
285 canceller_[i],
286 &FilesystemCopierAction::StaticAsyncReadReadyCallback,
287 this);
288 reading = true;
289 buffer_state_[i] = kBufferStateReading;
Darin Petkov3aefa862010-12-07 14:45:00 -0800290 } else if (!writing && !verify_hash_ &&
291 buffer_state_[i] == kBufferStateFull) {
Darin Petkovc2e4a7d2010-12-02 14:47:06 -0800292 g_output_stream_write_async(
293 dst_stream_,
294 buffer_[i].data(),
295 buffer_valid_size_[i],
296 G_PRIORITY_DEFAULT,
297 canceller_[i],
298 &FilesystemCopierAction::StaticAsyncWriteReadyCallback,
299 this);
300 writing = true;
301 buffer_state_[i] = kBufferStateWriting;
302 }
303 }
304 if (!reading && !writing) {
305 // We're done!
Gilad Arnoldd1c4d2d2014-06-05 14:07:53 -0700306 ErrorCode code = ErrorCode::kSuccess;
Darin Petkovc2e4a7d2010-12-02 14:47:06 -0800307 if (hasher_.Finalize()) {
Darin Petkov3aefa862010-12-07 14:45:00 -0800308 LOG(INFO) << "Hash: " << hasher_.hash();
309 if (verify_hash_) {
310 if (copying_kernel_install_path_) {
311 if (install_plan_.kernel_hash != hasher_.raw_hash()) {
Gilad Arnoldd1c4d2d2014-06-05 14:07:53 -0700312 code = ErrorCode::kNewKernelVerificationError;
Darin Petkov3aefa862010-12-07 14:45:00 -0800313 LOG(ERROR) << "New kernel verification failed.";
314 }
315 } else {
316 if (install_plan_.rootfs_hash != hasher_.raw_hash()) {
Gilad Arnoldd1c4d2d2014-06-05 14:07:53 -0700317 code = ErrorCode::kNewRootfsVerificationError;
Darin Petkov3aefa862010-12-07 14:45:00 -0800318 LOG(ERROR) << "New rootfs verification failed.";
319 }
320 }
Darin Petkov698d0412010-10-13 10:59:44 -0700321 } else {
Darin Petkov3aefa862010-12-07 14:45:00 -0800322 if (copying_kernel_install_path_) {
323 install_plan_.kernel_hash = hasher_.raw_hash();
324 } else {
325 install_plan_.rootfs_hash = hasher_.raw_hash();
326 }
Darin Petkov698d0412010-10-13 10:59:44 -0700327 }
adlr@google.com3defe6a2009-12-04 20:57:17 +0000328 } else {
Darin Petkovc2e4a7d2010-12-02 14:47:06 -0800329 LOG(ERROR) << "Unable to finalize the hash.";
Gilad Arnoldd1c4d2d2014-06-05 14:07:53 -0700330 code = ErrorCode::kError;
adlr@google.com3defe6a2009-12-04 20:57:17 +0000331 }
Darin Petkov3aefa862010-12-07 14:45:00 -0800332 Cleanup(code);
adlr@google.com3defe6a2009-12-04 20:57:17 +0000333 }
Andrew de los Reyesc7020782010-04-28 10:46:04 -0700334}
adlr@google.com3defe6a2009-12-04 20:57:17 +0000335
Darin Petkov698d0412010-10-13 10:59:44 -0700336void FilesystemCopierAction::DetermineFilesystemSize(int fd) {
Darin Petkov3aefa862010-12-07 14:45:00 -0800337 if (verify_hash_) {
338 filesystem_size_ = copying_kernel_install_path_ ?
339 install_plan_.kernel_size : install_plan_.rootfs_size;
340 LOG(INFO) << "Filesystem size: " << filesystem_size_;
341 return;
342 }
Darin Petkov698d0412010-10-13 10:59:44 -0700343 filesystem_size_ = kint64max;
344 int block_count = 0, block_size = 0;
345 if (!copying_kernel_install_path_ &&
346 utils::GetFilesystemSizeFromFD(fd, &block_count, &block_size)) {
347 filesystem_size_ = static_cast<int64_t>(block_count) * block_size;
348 LOG(INFO) << "Filesystem size: " << filesystem_size_ << " bytes ("
349 << block_count << "x" << block_size << ").";
350 }
351}
352
adlr@google.com3defe6a2009-12-04 20:57:17 +0000353} // namespace chromeos_update_engine