blob: 23d3b0ebca909780eaa0929e855aa50d589ef966 [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 Deymo42432912013-07-12 20:21:15 -070023#include "update_engine/hardware_interface.h"
adlr@google.com3defe6a2009-12-04 20:57:17 +000024#include "update_engine/subprocess.h"
Alex Deymo42432912013-07-12 20:21:15 -070025#include "update_engine/system_state.h"
adlr@google.com3defe6a2009-12-04 20:57:17 +000026#include "update_engine/utils.h"
27
Andrew de los Reyes4fe15d02009-12-10 19:01:36 -080028using std::map;
adlr@google.com3defe6a2009-12-04 20:57:17 +000029using std::min;
30using std::string;
31using std::vector;
32
33namespace chromeos_update_engine {
34
35namespace {
Darin Petkov3aefa862010-12-07 14:45:00 -080036const off_t kCopyFileBufferSize = 128 * 1024;
Alex Vakulenkod2779df2014-06-16 13:19:00 -070037} // namespace
adlr@google.com3defe6a2009-12-04 20:57:17 +000038
Darin Petkovc2e4a7d2010-12-02 14:47:06 -080039FilesystemCopierAction::FilesystemCopierAction(
Alex Deymo42432912013-07-12 20:21:15 -070040 SystemState* system_state,
Gilad Arnold581c2ea2012-07-19 12:33:49 -070041 bool copying_kernel_install_path,
42 bool verify_hash)
Darin Petkovc2e4a7d2010-12-02 14:47:06 -080043 : copying_kernel_install_path_(copying_kernel_install_path),
Darin Petkov3aefa862010-12-07 14:45:00 -080044 verify_hash_(verify_hash),
Darin Petkovc2e4a7d2010-12-02 14:47:06 -080045 src_stream_(NULL),
46 dst_stream_(NULL),
47 read_done_(false),
48 failed_(false),
49 cancelled_(false),
Alex Deymo42432912013-07-12 20:21:15 -070050 filesystem_size_(kint64max),
51 system_state_(system_state) {
Darin Petkovc2e4a7d2010-12-02 14:47:06 -080052 // A lot of code works on the implicit assumption that processing is done on
53 // exactly 2 ping-pong buffers.
54 COMPILE_ASSERT(arraysize(buffer_) == 2 &&
55 arraysize(buffer_state_) == 2 &&
56 arraysize(buffer_valid_size_) == 2 &&
57 arraysize(canceller_) == 2,
58 ping_pong_buffers_not_two);
59 for (int i = 0; i < 2; ++i) {
60 buffer_state_[i] = kBufferStateEmpty;
61 buffer_valid_size_[i] = 0;
62 canceller_[i] = NULL;
63 }
64}
65
adlr@google.com3defe6a2009-12-04 20:57:17 +000066void FilesystemCopierAction::PerformAction() {
Andrew de los Reyesc7020782010-04-28 10:46:04 -070067 // Will tell the ActionProcessor we've failed if we return.
68 ScopedActionCompleter abort_action_completer(processor_, this);
69
adlr@google.com3defe6a2009-12-04 20:57:17 +000070 if (!HasInputObject()) {
Andrew de los Reyesc7020782010-04-28 10:46:04 -070071 LOG(ERROR) << "FilesystemCopierAction missing input object.";
adlr@google.com3defe6a2009-12-04 20:57:17 +000072 return;
73 }
74 install_plan_ = GetInputObject();
Don Garrett83692e42013-11-08 10:11:30 -080075
76 // No need to copy on a resume.
77 if (!verify_hash_ && install_plan_.is_resume) {
Darin Petkov3aefa862010-12-07 14:45:00 -080078 // No copy or hash verification needed. Done!
Don Garrett83692e42013-11-08 10:11:30 -080079 LOG(INFO) << "filesystem copying skipped on resumed update.";
80 if (HasOutputPipe())
81 SetOutputObject(install_plan_);
Gilad Arnoldd1c4d2d2014-06-05 14:07:53 -070082 abort_action_completer.set_code(ErrorCode::kSuccess);
Don Garrett83692e42013-11-08 10:11:30 -080083 return;
84 }
85
86 if (copying_kernel_install_path_) {
87 if (!system_state_->hardware()->MarkKernelUnbootable(
88 install_plan_.kernel_install_path)) {
89 PLOG(ERROR) << "Unable to clear kernel GPT boot flags: " <<
90 install_plan_.kernel_install_path;
91 }
92 }
93
94 if (!verify_hash_ && install_plan_.is_full_update) {
95 // No copy or hash verification needed. Done!
96 LOG(INFO) << "filesystem copying skipped on full update.";
Andrew de los Reyesf98bff82010-05-06 13:33:25 -070097 if (HasOutputPipe())
98 SetOutputObject(install_plan_);
Gilad Arnoldd1c4d2d2014-06-05 14:07:53 -070099 abort_action_completer.set_code(ErrorCode::kSuccess);
adlr@google.com3defe6a2009-12-04 20:57:17 +0000100 return;
101 }
102
Darin Petkov3aefa862010-12-07 14:45:00 -0800103 const string destination = copying_kernel_install_path_ ?
104 install_plan_.kernel_install_path :
105 install_plan_.install_path;
106 string source = verify_hash_ ? destination : copy_source_;
Andrew de los Reyesf9185172010-05-03 11:07:05 -0700107 if (source.empty()) {
108 source = copying_kernel_install_path_ ?
J. Richard Barnette30842932013-10-28 15:04:23 -0700109 utils::KernelDeviceOfBootDevice(
Alex Deymo42432912013-07-12 20:21:15 -0700110 system_state_->hardware()->BootDevice()) :
111 system_state_->hardware()->BootDevice();
Andrew de los Reyesf9185172010-05-03 11:07:05 -0700112 }
Andrew de los Reyesc7020782010-04-28 10:46:04 -0700113 int src_fd = open(source.c_str(), O_RDONLY);
114 if (src_fd < 0) {
115 PLOG(ERROR) << "Unable to open " << source << " for reading:";
116 return;
117 }
Darin Petkov3aefa862010-12-07 14:45:00 -0800118
119 if (!verify_hash_) {
120 int dst_fd = open(destination.c_str(),
121 O_WRONLY | O_TRUNC | O_CREAT,
Andrew de los Reyesc7020782010-04-28 10:46:04 -0700122 0644);
Darin Petkov3aefa862010-12-07 14:45:00 -0800123 if (dst_fd < 0) {
124 close(src_fd);
125 PLOG(ERROR) << "Unable to open " << install_plan_.install_path
126 << " for writing:";
127 return;
128 }
129
130 dst_stream_ = g_unix_output_stream_new(dst_fd, TRUE);
adlr@google.com3defe6a2009-12-04 20:57:17 +0000131 }
132
Darin Petkov698d0412010-10-13 10:59:44 -0700133 DetermineFilesystemSize(src_fd);
Andrew de los Reyesc7020782010-04-28 10:46:04 -0700134 src_stream_ = g_unix_input_stream_new(src_fd, TRUE);
Andrew de los Reyesc7020782010-04-28 10:46:04 -0700135
Darin Petkovc2e4a7d2010-12-02 14:47:06 -0800136 for (int i = 0; i < 2; i++) {
137 buffer_[i].resize(kCopyFileBufferSize);
138 canceller_[i] = g_cancellable_new();
139 }
Andrew de los Reyesc7020782010-04-28 10:46:04 -0700140
Darin Petkovc2e4a7d2010-12-02 14:47:06 -0800141 // Start the first read.
142 SpawnAsyncActions();
Andrew de los Reyesc7020782010-04-28 10:46:04 -0700143
144 abort_action_completer.set_should_complete(false);
adlr@google.com3defe6a2009-12-04 20:57:17 +0000145}
146
147void FilesystemCopierAction::TerminateProcessing() {
Darin Petkovc2e4a7d2010-12-02 14:47:06 -0800148 for (int i = 0; i < 2; i++) {
149 if (canceller_[i]) {
150 g_cancellable_cancel(canceller_[i]);
151 }
adlr@google.com3defe6a2009-12-04 20:57:17 +0000152 }
153}
154
Ben Chan2add7d72012-10-08 19:28:37 -0700155bool FilesystemCopierAction::IsCleanupPending() const {
156 return (src_stream_ != NULL);
157}
158
David Zeuthena99981f2013-04-29 13:42:47 -0700159void FilesystemCopierAction::Cleanup(ErrorCode code) {
Darin Petkovc2e4a7d2010-12-02 14:47:06 -0800160 for (int i = 0; i < 2; i++) {
161 g_object_unref(canceller_[i]);
162 canceller_[i] = NULL;
163 }
Andrew de los Reyesc7020782010-04-28 10:46:04 -0700164 g_object_unref(src_stream_);
165 src_stream_ = NULL;
Darin Petkov3aefa862010-12-07 14:45:00 -0800166 if (dst_stream_) {
167 g_object_unref(dst_stream_);
168 dst_stream_ = NULL;
169 }
Darin Petkovc2e4a7d2010-12-02 14:47:06 -0800170 if (cancelled_)
Andrew de los Reyesc7020782010-04-28 10:46:04 -0700171 return;
Gilad Arnoldd1c4d2d2014-06-05 14:07:53 -0700172 if (code == ErrorCode::kSuccess && HasOutputPipe())
adlr@google.com3defe6a2009-12-04 20:57:17 +0000173 SetOutputObject(install_plan_);
Darin Petkov3aefa862010-12-07 14:45:00 -0800174 processor_->ActionComplete(this, code);
adlr@google.com3defe6a2009-12-04 20:57:17 +0000175}
176
Darin Petkovc2e4a7d2010-12-02 14:47:06 -0800177void FilesystemCopierAction::AsyncReadReadyCallback(GObject *source_object,
178 GAsyncResult *res) {
179 int index = buffer_state_[0] == kBufferStateReading ? 0 : 1;
180 CHECK(buffer_state_[index] == kBufferStateReading);
181
Andrew de los Reyesc7020782010-04-28 10:46:04 -0700182 GError* error = NULL;
Darin Petkovc2e4a7d2010-12-02 14:47:06 -0800183 CHECK(canceller_[index]);
184 cancelled_ = g_cancellable_is_cancelled(canceller_[index]) == TRUE;
adlr@google.com3defe6a2009-12-04 20:57:17 +0000185
Darin Petkovc2e4a7d2010-12-02 14:47:06 -0800186 ssize_t bytes_read = g_input_stream_read_finish(src_stream_, res, &error);
187 if (bytes_read < 0) {
Darin Petkova0b9e772011-10-06 05:05:56 -0700188 LOG(ERROR) << "Read failed: " << utils::GetAndFreeGError(&error);
Darin Petkovc2e4a7d2010-12-02 14:47:06 -0800189 failed_ = true;
190 buffer_state_[index] = kBufferStateEmpty;
191 } else if (bytes_read == 0) {
192 read_done_ = true;
193 buffer_state_[index] = kBufferStateEmpty;
194 } else {
195 buffer_valid_size_[index] = bytes_read;
196 buffer_state_[index] = kBufferStateFull;
Darin Petkovc2e4a7d2010-12-02 14:47:06 -0800197 filesystem_size_ -= bytes_read;
198 }
Darin Petkovc2e4a7d2010-12-02 14:47:06 -0800199 SpawnAsyncActions();
200
201 if (bytes_read > 0) {
Darin Petkov3aefa862010-12-07 14:45:00 -0800202 // If read_done_ is set, SpawnAsyncActions may finalize the hash so the hash
203 // update below would happen too late.
204 CHECK(!read_done_);
Darin Petkovc2e4a7d2010-12-02 14:47:06 -0800205 if (!hasher_.Update(buffer_[index].data(), bytes_read)) {
206 LOG(ERROR) << "Unable to update the hash.";
207 failed_ = true;
adlr@google.com3defe6a2009-12-04 20:57:17 +0000208 }
Darin Petkov3aefa862010-12-07 14:45:00 -0800209 if (verify_hash_) {
210 buffer_state_[index] = kBufferStateEmpty;
211 }
Darin Petkovc2e4a7d2010-12-02 14:47:06 -0800212 }
213}
Andrew de los Reyesc7020782010-04-28 10:46:04 -0700214
Darin Petkovc2e4a7d2010-12-02 14:47:06 -0800215void FilesystemCopierAction::StaticAsyncReadReadyCallback(
216 GObject *source_object,
217 GAsyncResult *res,
218 gpointer user_data) {
219 reinterpret_cast<FilesystemCopierAction*>(user_data)->
220 AsyncReadReadyCallback(source_object, res);
221}
222
223void FilesystemCopierAction::AsyncWriteReadyCallback(GObject *source_object,
224 GAsyncResult *res) {
225 int index = buffer_state_[0] == kBufferStateWriting ? 0 : 1;
226 CHECK(buffer_state_[index] == kBufferStateWriting);
Gilad Arnold581c2ea2012-07-19 12:33:49 -0700227 buffer_state_[index] = kBufferStateEmpty;
Darin Petkovc2e4a7d2010-12-02 14:47:06 -0800228
229 GError* error = NULL;
230 CHECK(canceller_[index]);
231 cancelled_ = g_cancellable_is_cancelled(canceller_[index]) == TRUE;
232
233 ssize_t bytes_written = g_output_stream_write_finish(dst_stream_,
234 res,
235 &error);
Gilad Arnold6dbbd392012-07-10 16:19:11 -0700236
Darin Petkovc2e4a7d2010-12-02 14:47:06 -0800237 if (bytes_written < static_cast<ssize_t>(buffer_valid_size_[index])) {
238 if (bytes_written < 0) {
Gilad Arnold6dbbd392012-07-10 16:19:11 -0700239 LOG(ERROR) << "Write error: " << utils::GetAndFreeGError(&error);
Darin Petkovc2e4a7d2010-12-02 14:47:06 -0800240 } else {
Gilad Arnold6dbbd392012-07-10 16:19:11 -0700241 LOG(ERROR) << "Wrote too few bytes: " << bytes_written
Gilad Arnold581c2ea2012-07-19 12:33:49 -0700242 << " < " << buffer_valid_size_[index];
Darin Petkovc2e4a7d2010-12-02 14:47:06 -0800243 }
Gilad Arnold581c2ea2012-07-19 12:33:49 -0700244 failed_ = true;
Darin Petkovc2e4a7d2010-12-02 14:47:06 -0800245 }
246
247 SpawnAsyncActions();
248}
249
250void FilesystemCopierAction::StaticAsyncWriteReadyCallback(
251 GObject *source_object,
252 GAsyncResult *res,
253 gpointer user_data) {
254 reinterpret_cast<FilesystemCopierAction*>(user_data)->
255 AsyncWriteReadyCallback(source_object, res);
256}
257
258void FilesystemCopierAction::SpawnAsyncActions() {
259 bool reading = false;
260 bool writing = false;
261 for (int i = 0; i < 2; i++) {
262 if (buffer_state_[i] == kBufferStateReading) {
263 reading = true;
264 }
265 if (buffer_state_[i] == kBufferStateWriting) {
266 writing = true;
267 }
268 }
269 if (failed_ || cancelled_) {
270 if (!reading && !writing) {
Gilad Arnoldd1c4d2d2014-06-05 14:07:53 -0700271 Cleanup(ErrorCode::kError);
Darin Petkovc2e4a7d2010-12-02 14:47:06 -0800272 }
273 return;
274 }
275 for (int i = 0; i < 2; i++) {
276 if (!reading && !read_done_ && buffer_state_[i] == kBufferStateEmpty) {
Darin Petkov3aefa862010-12-07 14:45:00 -0800277 int64_t bytes_to_read = std::min(static_cast<int64_t>(buffer_[0].size()),
278 filesystem_size_);
Darin Petkovc2e4a7d2010-12-02 14:47:06 -0800279 g_input_stream_read_async(
280 src_stream_,
281 buffer_[i].data(),
Darin Petkov3aefa862010-12-07 14:45:00 -0800282 bytes_to_read,
Darin Petkovc2e4a7d2010-12-02 14:47:06 -0800283 G_PRIORITY_DEFAULT,
284 canceller_[i],
285 &FilesystemCopierAction::StaticAsyncReadReadyCallback,
286 this);
287 reading = true;
288 buffer_state_[i] = kBufferStateReading;
Darin Petkov3aefa862010-12-07 14:45:00 -0800289 } else if (!writing && !verify_hash_ &&
290 buffer_state_[i] == kBufferStateFull) {
Darin Petkovc2e4a7d2010-12-02 14:47:06 -0800291 g_output_stream_write_async(
292 dst_stream_,
293 buffer_[i].data(),
294 buffer_valid_size_[i],
295 G_PRIORITY_DEFAULT,
296 canceller_[i],
297 &FilesystemCopierAction::StaticAsyncWriteReadyCallback,
298 this);
299 writing = true;
300 buffer_state_[i] = kBufferStateWriting;
301 }
302 }
303 if (!reading && !writing) {
304 // We're done!
Gilad Arnoldd1c4d2d2014-06-05 14:07:53 -0700305 ErrorCode code = ErrorCode::kSuccess;
Darin Petkovc2e4a7d2010-12-02 14:47:06 -0800306 if (hasher_.Finalize()) {
Darin Petkov3aefa862010-12-07 14:45:00 -0800307 LOG(INFO) << "Hash: " << hasher_.hash();
308 if (verify_hash_) {
309 if (copying_kernel_install_path_) {
310 if (install_plan_.kernel_hash != hasher_.raw_hash()) {
Gilad Arnoldd1c4d2d2014-06-05 14:07:53 -0700311 code = ErrorCode::kNewKernelVerificationError;
Darin Petkov3aefa862010-12-07 14:45:00 -0800312 LOG(ERROR) << "New kernel verification failed.";
313 }
314 } else {
315 if (install_plan_.rootfs_hash != hasher_.raw_hash()) {
Gilad Arnoldd1c4d2d2014-06-05 14:07:53 -0700316 code = ErrorCode::kNewRootfsVerificationError;
Darin Petkov3aefa862010-12-07 14:45:00 -0800317 LOG(ERROR) << "New rootfs verification failed.";
318 }
319 }
Darin Petkov698d0412010-10-13 10:59:44 -0700320 } else {
Darin Petkov3aefa862010-12-07 14:45:00 -0800321 if (copying_kernel_install_path_) {
322 install_plan_.kernel_hash = hasher_.raw_hash();
323 } else {
324 install_plan_.rootfs_hash = hasher_.raw_hash();
325 }
Darin Petkov698d0412010-10-13 10:59:44 -0700326 }
adlr@google.com3defe6a2009-12-04 20:57:17 +0000327 } else {
Darin Petkovc2e4a7d2010-12-02 14:47:06 -0800328 LOG(ERROR) << "Unable to finalize the hash.";
Gilad Arnoldd1c4d2d2014-06-05 14:07:53 -0700329 code = ErrorCode::kError;
adlr@google.com3defe6a2009-12-04 20:57:17 +0000330 }
Darin Petkov3aefa862010-12-07 14:45:00 -0800331 Cleanup(code);
adlr@google.com3defe6a2009-12-04 20:57:17 +0000332 }
Andrew de los Reyesc7020782010-04-28 10:46:04 -0700333}
adlr@google.com3defe6a2009-12-04 20:57:17 +0000334
Darin Petkov698d0412010-10-13 10:59:44 -0700335void FilesystemCopierAction::DetermineFilesystemSize(int fd) {
Darin Petkov3aefa862010-12-07 14:45:00 -0800336 if (verify_hash_) {
337 filesystem_size_ = copying_kernel_install_path_ ?
338 install_plan_.kernel_size : install_plan_.rootfs_size;
339 LOG(INFO) << "Filesystem size: " << filesystem_size_;
340 return;
341 }
Darin Petkov698d0412010-10-13 10:59:44 -0700342 filesystem_size_ = kint64max;
343 int block_count = 0, block_size = 0;
344 if (!copying_kernel_install_path_ &&
345 utils::GetFilesystemSizeFromFD(fd, &block_count, &block_size)) {
346 filesystem_size_ = static_cast<int64_t>(block_count) * block_size;
347 LOG(INFO) << "Filesystem size: " << filesystem_size_ << " bytes ("
348 << block_count << "x" << block_size << ").";
349 }
350}
351
adlr@google.com3defe6a2009-12-04 20:57:17 +0000352} // namespace chromeos_update_engine