blob: c9be9629a408f605ea28f6603aea672a62be2302 [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 <sys/stat.h>
8#include <sys/types.h>
9#include <errno.h>
10#include <fcntl.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
adlr@google.com3defe6a2009-12-04 20:57:17 +000023#include "update_engine/filesystem_iterator.h"
24#include "update_engine/subprocess.h"
25#include "update_engine/utils.h"
26
Andrew de los Reyes4fe15d02009-12-10 19:01:36 -080027using std::map;
adlr@google.com3defe6a2009-12-04 20:57:17 +000028using std::min;
29using std::string;
30using std::vector;
31
32namespace chromeos_update_engine {
33
34namespace {
Darin Petkov3aefa862010-12-07 14:45:00 -080035const off_t kCopyFileBufferSize = 128 * 1024;
adlr@google.com3defe6a2009-12-04 20:57:17 +000036} // namespace {}
37
Darin Petkovc2e4a7d2010-12-02 14:47:06 -080038FilesystemCopierAction::FilesystemCopierAction(
Gilad Arnold581c2ea2012-07-19 12:33:49 -070039 bool copying_kernel_install_path,
40 bool verify_hash)
Darin Petkovc2e4a7d2010-12-02 14:47:06 -080041 : copying_kernel_install_path_(copying_kernel_install_path),
Darin Petkov3aefa862010-12-07 14:45:00 -080042 verify_hash_(verify_hash),
Darin Petkovc2e4a7d2010-12-02 14:47:06 -080043 src_stream_(NULL),
44 dst_stream_(NULL),
45 read_done_(false),
46 failed_(false),
47 cancelled_(false),
Gilad Arnold581c2ea2012-07-19 12:33:49 -070048 filesystem_size_(kint64max) {
Darin Petkovc2e4a7d2010-12-02 14:47:06 -080049 // A lot of code works on the implicit assumption that processing is done on
50 // exactly 2 ping-pong buffers.
51 COMPILE_ASSERT(arraysize(buffer_) == 2 &&
52 arraysize(buffer_state_) == 2 &&
53 arraysize(buffer_valid_size_) == 2 &&
54 arraysize(canceller_) == 2,
55 ping_pong_buffers_not_two);
56 for (int i = 0; i < 2; ++i) {
57 buffer_state_[i] = kBufferStateEmpty;
58 buffer_valid_size_[i] = 0;
59 canceller_[i] = NULL;
60 }
61}
62
adlr@google.com3defe6a2009-12-04 20:57:17 +000063void FilesystemCopierAction::PerformAction() {
Andrew de los Reyesc7020782010-04-28 10:46:04 -070064 // Will tell the ActionProcessor we've failed if we return.
65 ScopedActionCompleter abort_action_completer(processor_, this);
66
adlr@google.com3defe6a2009-12-04 20:57:17 +000067 if (!HasInputObject()) {
Andrew de los Reyesc7020782010-04-28 10:46:04 -070068 LOG(ERROR) << "FilesystemCopierAction missing input object.";
adlr@google.com3defe6a2009-12-04 20:57:17 +000069 return;
70 }
71 install_plan_ = GetInputObject();
Darin Petkov7ed561b2011-10-04 02:59:03 -070072 if (!verify_hash_ && install_plan_.is_resume) {
Darin Petkov3aefa862010-12-07 14:45:00 -080073 // No copy or hash verification needed. Done!
Andrew de los Reyesf98bff82010-05-06 13:33:25 -070074 if (HasOutputPipe())
75 SetOutputObject(install_plan_);
Darin Petkovc1a8b422010-07-19 11:34:49 -070076 abort_action_completer.set_code(kActionCodeSuccess);
adlr@google.com3defe6a2009-12-04 20:57:17 +000077 return;
78 }
79
Darin Petkov3aefa862010-12-07 14:45:00 -080080 const string destination = copying_kernel_install_path_ ?
81 install_plan_.kernel_install_path :
82 install_plan_.install_path;
83 string source = verify_hash_ ? destination : copy_source_;
Andrew de los Reyesf9185172010-05-03 11:07:05 -070084 if (source.empty()) {
85 source = copying_kernel_install_path_ ?
86 utils::BootKernelDevice(utils::BootDevice()) :
87 utils::BootDevice();
88 }
Andrew de los Reyesc7020782010-04-28 10:46:04 -070089 int src_fd = open(source.c_str(), O_RDONLY);
90 if (src_fd < 0) {
91 PLOG(ERROR) << "Unable to open " << source << " for reading:";
92 return;
93 }
Darin Petkov3aefa862010-12-07 14:45:00 -080094
95 if (!verify_hash_) {
96 int dst_fd = open(destination.c_str(),
97 O_WRONLY | O_TRUNC | O_CREAT,
Andrew de los Reyesc7020782010-04-28 10:46:04 -070098 0644);
Darin Petkov3aefa862010-12-07 14:45:00 -080099 if (dst_fd < 0) {
100 close(src_fd);
101 PLOG(ERROR) << "Unable to open " << install_plan_.install_path
102 << " for writing:";
103 return;
104 }
105
106 dst_stream_ = g_unix_output_stream_new(dst_fd, TRUE);
adlr@google.com3defe6a2009-12-04 20:57:17 +0000107 }
108
Darin Petkov698d0412010-10-13 10:59:44 -0700109 DetermineFilesystemSize(src_fd);
Andrew de los Reyesc7020782010-04-28 10:46:04 -0700110 src_stream_ = g_unix_input_stream_new(src_fd, TRUE);
Andrew de los Reyesc7020782010-04-28 10:46:04 -0700111
Darin Petkovc2e4a7d2010-12-02 14:47:06 -0800112 for (int i = 0; i < 2; i++) {
113 buffer_[i].resize(kCopyFileBufferSize);
114 canceller_[i] = g_cancellable_new();
115 }
Andrew de los Reyesc7020782010-04-28 10:46:04 -0700116
Darin Petkovc2e4a7d2010-12-02 14:47:06 -0800117 // Start the first read.
118 SpawnAsyncActions();
Andrew de los Reyesc7020782010-04-28 10:46:04 -0700119
120 abort_action_completer.set_should_complete(false);
adlr@google.com3defe6a2009-12-04 20:57:17 +0000121}
122
123void FilesystemCopierAction::TerminateProcessing() {
Darin Petkovc2e4a7d2010-12-02 14:47:06 -0800124 for (int i = 0; i < 2; i++) {
125 if (canceller_[i]) {
126 g_cancellable_cancel(canceller_[i]);
127 }
adlr@google.com3defe6a2009-12-04 20:57:17 +0000128 }
129}
130
Darin Petkov3aefa862010-12-07 14:45:00 -0800131void FilesystemCopierAction::Cleanup(ActionExitCode code) {
Darin Petkovc2e4a7d2010-12-02 14:47:06 -0800132 for (int i = 0; i < 2; i++) {
133 g_object_unref(canceller_[i]);
134 canceller_[i] = NULL;
135 }
Andrew de los Reyesc7020782010-04-28 10:46:04 -0700136 g_object_unref(src_stream_);
137 src_stream_ = NULL;
Darin Petkov3aefa862010-12-07 14:45:00 -0800138 if (dst_stream_) {
139 g_object_unref(dst_stream_);
140 dst_stream_ = NULL;
141 }
Darin Petkovc2e4a7d2010-12-02 14:47:06 -0800142 if (cancelled_)
Andrew de los Reyesc7020782010-04-28 10:46:04 -0700143 return;
Darin Petkov3aefa862010-12-07 14:45:00 -0800144 if (code == kActionCodeSuccess && HasOutputPipe())
adlr@google.com3defe6a2009-12-04 20:57:17 +0000145 SetOutputObject(install_plan_);
Darin Petkov3aefa862010-12-07 14:45:00 -0800146 processor_->ActionComplete(this, code);
adlr@google.com3defe6a2009-12-04 20:57:17 +0000147}
148
Darin Petkovc2e4a7d2010-12-02 14:47:06 -0800149void FilesystemCopierAction::AsyncReadReadyCallback(GObject *source_object,
150 GAsyncResult *res) {
151 int index = buffer_state_[0] == kBufferStateReading ? 0 : 1;
152 CHECK(buffer_state_[index] == kBufferStateReading);
153
Andrew de los Reyesc7020782010-04-28 10:46:04 -0700154 GError* error = NULL;
Darin Petkovc2e4a7d2010-12-02 14:47:06 -0800155 CHECK(canceller_[index]);
156 cancelled_ = g_cancellable_is_cancelled(canceller_[index]) == TRUE;
adlr@google.com3defe6a2009-12-04 20:57:17 +0000157
Darin Petkovc2e4a7d2010-12-02 14:47:06 -0800158 ssize_t bytes_read = g_input_stream_read_finish(src_stream_, res, &error);
159 if (bytes_read < 0) {
Darin Petkova0b9e772011-10-06 05:05:56 -0700160 LOG(ERROR) << "Read failed: " << utils::GetAndFreeGError(&error);
Darin Petkovc2e4a7d2010-12-02 14:47:06 -0800161 failed_ = true;
162 buffer_state_[index] = kBufferStateEmpty;
163 } else if (bytes_read == 0) {
164 read_done_ = true;
165 buffer_state_[index] = kBufferStateEmpty;
166 } else {
167 buffer_valid_size_[index] = bytes_read;
168 buffer_state_[index] = kBufferStateFull;
Darin Petkovc2e4a7d2010-12-02 14:47:06 -0800169 filesystem_size_ -= bytes_read;
170 }
Darin Petkovc2e4a7d2010-12-02 14:47:06 -0800171 SpawnAsyncActions();
172
173 if (bytes_read > 0) {
Darin Petkov3aefa862010-12-07 14:45:00 -0800174 // If read_done_ is set, SpawnAsyncActions may finalize the hash so the hash
175 // update below would happen too late.
176 CHECK(!read_done_);
Darin Petkovc2e4a7d2010-12-02 14:47:06 -0800177 if (!hasher_.Update(buffer_[index].data(), bytes_read)) {
178 LOG(ERROR) << "Unable to update the hash.";
179 failed_ = true;
adlr@google.com3defe6a2009-12-04 20:57:17 +0000180 }
Darin Petkov3aefa862010-12-07 14:45:00 -0800181 if (verify_hash_) {
182 buffer_state_[index] = kBufferStateEmpty;
183 }
Darin Petkovc2e4a7d2010-12-02 14:47:06 -0800184 }
185}
Andrew de los Reyesc7020782010-04-28 10:46:04 -0700186
Darin Petkovc2e4a7d2010-12-02 14:47:06 -0800187void FilesystemCopierAction::StaticAsyncReadReadyCallback(
188 GObject *source_object,
189 GAsyncResult *res,
190 gpointer user_data) {
191 reinterpret_cast<FilesystemCopierAction*>(user_data)->
192 AsyncReadReadyCallback(source_object, res);
193}
194
195void FilesystemCopierAction::AsyncWriteReadyCallback(GObject *source_object,
196 GAsyncResult *res) {
197 int index = buffer_state_[0] == kBufferStateWriting ? 0 : 1;
198 CHECK(buffer_state_[index] == kBufferStateWriting);
Gilad Arnold581c2ea2012-07-19 12:33:49 -0700199 buffer_state_[index] = kBufferStateEmpty;
Darin Petkovc2e4a7d2010-12-02 14:47:06 -0800200
201 GError* error = NULL;
202 CHECK(canceller_[index]);
203 cancelled_ = g_cancellable_is_cancelled(canceller_[index]) == TRUE;
204
205 ssize_t bytes_written = g_output_stream_write_finish(dst_stream_,
206 res,
207 &error);
Gilad Arnold6dbbd392012-07-10 16:19:11 -0700208
Darin Petkovc2e4a7d2010-12-02 14:47:06 -0800209 if (bytes_written < static_cast<ssize_t>(buffer_valid_size_[index])) {
210 if (bytes_written < 0) {
Gilad Arnold6dbbd392012-07-10 16:19:11 -0700211 LOG(ERROR) << "Write error: " << utils::GetAndFreeGError(&error);
Darin Petkovc2e4a7d2010-12-02 14:47:06 -0800212 } else {
Gilad Arnold6dbbd392012-07-10 16:19:11 -0700213 LOG(ERROR) << "Wrote too few bytes: " << bytes_written
Gilad Arnold581c2ea2012-07-19 12:33:49 -0700214 << " < " << buffer_valid_size_[index];
Darin Petkovc2e4a7d2010-12-02 14:47:06 -0800215 }
Gilad Arnold581c2ea2012-07-19 12:33:49 -0700216 failed_ = true;
Darin Petkovc2e4a7d2010-12-02 14:47:06 -0800217 }
218
219 SpawnAsyncActions();
220}
221
222void FilesystemCopierAction::StaticAsyncWriteReadyCallback(
223 GObject *source_object,
224 GAsyncResult *res,
225 gpointer user_data) {
226 reinterpret_cast<FilesystemCopierAction*>(user_data)->
227 AsyncWriteReadyCallback(source_object, res);
228}
229
230void FilesystemCopierAction::SpawnAsyncActions() {
231 bool reading = false;
232 bool writing = false;
233 for (int i = 0; i < 2; i++) {
234 if (buffer_state_[i] == kBufferStateReading) {
235 reading = true;
236 }
237 if (buffer_state_[i] == kBufferStateWriting) {
238 writing = true;
239 }
240 }
241 if (failed_ || cancelled_) {
242 if (!reading && !writing) {
Darin Petkov3aefa862010-12-07 14:45:00 -0800243 Cleanup(kActionCodeError);
Darin Petkovc2e4a7d2010-12-02 14:47:06 -0800244 }
245 return;
246 }
247 for (int i = 0; i < 2; i++) {
248 if (!reading && !read_done_ && buffer_state_[i] == kBufferStateEmpty) {
Darin Petkov3aefa862010-12-07 14:45:00 -0800249 int64_t bytes_to_read = std::min(static_cast<int64_t>(buffer_[0].size()),
250 filesystem_size_);
Darin Petkovc2e4a7d2010-12-02 14:47:06 -0800251 g_input_stream_read_async(
252 src_stream_,
253 buffer_[i].data(),
Darin Petkov3aefa862010-12-07 14:45:00 -0800254 bytes_to_read,
Darin Petkovc2e4a7d2010-12-02 14:47:06 -0800255 G_PRIORITY_DEFAULT,
256 canceller_[i],
257 &FilesystemCopierAction::StaticAsyncReadReadyCallback,
258 this);
259 reading = true;
260 buffer_state_[i] = kBufferStateReading;
Darin Petkov3aefa862010-12-07 14:45:00 -0800261 } else if (!writing && !verify_hash_ &&
262 buffer_state_[i] == kBufferStateFull) {
Darin Petkovc2e4a7d2010-12-02 14:47:06 -0800263 g_output_stream_write_async(
264 dst_stream_,
265 buffer_[i].data(),
266 buffer_valid_size_[i],
267 G_PRIORITY_DEFAULT,
268 canceller_[i],
269 &FilesystemCopierAction::StaticAsyncWriteReadyCallback,
270 this);
271 writing = true;
272 buffer_state_[i] = kBufferStateWriting;
273 }
274 }
275 if (!reading && !writing) {
276 // We're done!
Darin Petkov3aefa862010-12-07 14:45:00 -0800277 ActionExitCode code = kActionCodeSuccess;
Darin Petkovc2e4a7d2010-12-02 14:47:06 -0800278 if (hasher_.Finalize()) {
Darin Petkov3aefa862010-12-07 14:45:00 -0800279 LOG(INFO) << "Hash: " << hasher_.hash();
280 if (verify_hash_) {
281 if (copying_kernel_install_path_) {
282 if (install_plan_.kernel_hash != hasher_.raw_hash()) {
283 code = kActionCodeNewKernelVerificationError;
284 LOG(ERROR) << "New kernel verification failed.";
285 }
286 } else {
287 if (install_plan_.rootfs_hash != hasher_.raw_hash()) {
288 code = kActionCodeNewRootfsVerificationError;
289 LOG(ERROR) << "New rootfs verification failed.";
290 }
291 }
Darin Petkov698d0412010-10-13 10:59:44 -0700292 } else {
Darin Petkov3aefa862010-12-07 14:45:00 -0800293 if (copying_kernel_install_path_) {
294 install_plan_.kernel_hash = hasher_.raw_hash();
295 } else {
296 install_plan_.rootfs_hash = hasher_.raw_hash();
297 }
Darin Petkov698d0412010-10-13 10:59:44 -0700298 }
adlr@google.com3defe6a2009-12-04 20:57:17 +0000299 } else {
Darin Petkovc2e4a7d2010-12-02 14:47:06 -0800300 LOG(ERROR) << "Unable to finalize the hash.";
Darin Petkov3aefa862010-12-07 14:45:00 -0800301 code = kActionCodeError;
adlr@google.com3defe6a2009-12-04 20:57:17 +0000302 }
Darin Petkov3aefa862010-12-07 14:45:00 -0800303 Cleanup(code);
adlr@google.com3defe6a2009-12-04 20:57:17 +0000304 }
Andrew de los Reyesc7020782010-04-28 10:46:04 -0700305}
adlr@google.com3defe6a2009-12-04 20:57:17 +0000306
Darin Petkov698d0412010-10-13 10:59:44 -0700307void FilesystemCopierAction::DetermineFilesystemSize(int fd) {
Darin Petkov3aefa862010-12-07 14:45:00 -0800308 if (verify_hash_) {
309 filesystem_size_ = copying_kernel_install_path_ ?
310 install_plan_.kernel_size : install_plan_.rootfs_size;
311 LOG(INFO) << "Filesystem size: " << filesystem_size_;
312 return;
313 }
Darin Petkov698d0412010-10-13 10:59:44 -0700314 filesystem_size_ = kint64max;
315 int block_count = 0, block_size = 0;
316 if (!copying_kernel_install_path_ &&
317 utils::GetFilesystemSizeFromFD(fd, &block_count, &block_size)) {
318 filesystem_size_ = static_cast<int64_t>(block_count) * block_size;
319 LOG(INFO) << "Filesystem size: " << filesystem_size_ << " bytes ("
320 << block_count << "x" << block_size << ").";
321 }
322}
323
adlr@google.com3defe6a2009-12-04 20:57:17 +0000324} // namespace chromeos_update_engine