blob: 1561f93743d5176d288282de503e1321e9efb793 [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
Ben Chan2add7d72012-10-08 19:28:37 -0700131bool FilesystemCopierAction::IsCleanupPending() const {
132 return (src_stream_ != NULL);
133}
134
Darin Petkov3aefa862010-12-07 14:45:00 -0800135void FilesystemCopierAction::Cleanup(ActionExitCode code) {
Darin Petkovc2e4a7d2010-12-02 14:47:06 -0800136 for (int i = 0; i < 2; i++) {
137 g_object_unref(canceller_[i]);
138 canceller_[i] = NULL;
139 }
Andrew de los Reyesc7020782010-04-28 10:46:04 -0700140 g_object_unref(src_stream_);
141 src_stream_ = NULL;
Darin Petkov3aefa862010-12-07 14:45:00 -0800142 if (dst_stream_) {
143 g_object_unref(dst_stream_);
144 dst_stream_ = NULL;
145 }
Darin Petkovc2e4a7d2010-12-02 14:47:06 -0800146 if (cancelled_)
Andrew de los Reyesc7020782010-04-28 10:46:04 -0700147 return;
Darin Petkov3aefa862010-12-07 14:45:00 -0800148 if (code == kActionCodeSuccess && HasOutputPipe())
adlr@google.com3defe6a2009-12-04 20:57:17 +0000149 SetOutputObject(install_plan_);
Darin Petkov3aefa862010-12-07 14:45:00 -0800150 processor_->ActionComplete(this, code);
adlr@google.com3defe6a2009-12-04 20:57:17 +0000151}
152
Darin Petkovc2e4a7d2010-12-02 14:47:06 -0800153void FilesystemCopierAction::AsyncReadReadyCallback(GObject *source_object,
154 GAsyncResult *res) {
155 int index = buffer_state_[0] == kBufferStateReading ? 0 : 1;
156 CHECK(buffer_state_[index] == kBufferStateReading);
157
Andrew de los Reyesc7020782010-04-28 10:46:04 -0700158 GError* error = NULL;
Darin Petkovc2e4a7d2010-12-02 14:47:06 -0800159 CHECK(canceller_[index]);
160 cancelled_ = g_cancellable_is_cancelled(canceller_[index]) == TRUE;
adlr@google.com3defe6a2009-12-04 20:57:17 +0000161
Darin Petkovc2e4a7d2010-12-02 14:47:06 -0800162 ssize_t bytes_read = g_input_stream_read_finish(src_stream_, res, &error);
163 if (bytes_read < 0) {
Darin Petkova0b9e772011-10-06 05:05:56 -0700164 LOG(ERROR) << "Read failed: " << utils::GetAndFreeGError(&error);
Darin Petkovc2e4a7d2010-12-02 14:47:06 -0800165 failed_ = true;
166 buffer_state_[index] = kBufferStateEmpty;
167 } else if (bytes_read == 0) {
168 read_done_ = true;
169 buffer_state_[index] = kBufferStateEmpty;
170 } else {
171 buffer_valid_size_[index] = bytes_read;
172 buffer_state_[index] = kBufferStateFull;
Darin Petkovc2e4a7d2010-12-02 14:47:06 -0800173 filesystem_size_ -= bytes_read;
174 }
Darin Petkovc2e4a7d2010-12-02 14:47:06 -0800175 SpawnAsyncActions();
176
177 if (bytes_read > 0) {
Darin Petkov3aefa862010-12-07 14:45:00 -0800178 // If read_done_ is set, SpawnAsyncActions may finalize the hash so the hash
179 // update below would happen too late.
180 CHECK(!read_done_);
Darin Petkovc2e4a7d2010-12-02 14:47:06 -0800181 if (!hasher_.Update(buffer_[index].data(), bytes_read)) {
182 LOG(ERROR) << "Unable to update the hash.";
183 failed_ = true;
adlr@google.com3defe6a2009-12-04 20:57:17 +0000184 }
Darin Petkov3aefa862010-12-07 14:45:00 -0800185 if (verify_hash_) {
186 buffer_state_[index] = kBufferStateEmpty;
187 }
Darin Petkovc2e4a7d2010-12-02 14:47:06 -0800188 }
189}
Andrew de los Reyesc7020782010-04-28 10:46:04 -0700190
Darin Petkovc2e4a7d2010-12-02 14:47:06 -0800191void FilesystemCopierAction::StaticAsyncReadReadyCallback(
192 GObject *source_object,
193 GAsyncResult *res,
194 gpointer user_data) {
195 reinterpret_cast<FilesystemCopierAction*>(user_data)->
196 AsyncReadReadyCallback(source_object, res);
197}
198
199void FilesystemCopierAction::AsyncWriteReadyCallback(GObject *source_object,
200 GAsyncResult *res) {
201 int index = buffer_state_[0] == kBufferStateWriting ? 0 : 1;
202 CHECK(buffer_state_[index] == kBufferStateWriting);
Gilad Arnold581c2ea2012-07-19 12:33:49 -0700203 buffer_state_[index] = kBufferStateEmpty;
Darin Petkovc2e4a7d2010-12-02 14:47:06 -0800204
205 GError* error = NULL;
206 CHECK(canceller_[index]);
207 cancelled_ = g_cancellable_is_cancelled(canceller_[index]) == TRUE;
208
209 ssize_t bytes_written = g_output_stream_write_finish(dst_stream_,
210 res,
211 &error);
Gilad Arnold6dbbd392012-07-10 16:19:11 -0700212
Darin Petkovc2e4a7d2010-12-02 14:47:06 -0800213 if (bytes_written < static_cast<ssize_t>(buffer_valid_size_[index])) {
214 if (bytes_written < 0) {
Gilad Arnold6dbbd392012-07-10 16:19:11 -0700215 LOG(ERROR) << "Write error: " << utils::GetAndFreeGError(&error);
Darin Petkovc2e4a7d2010-12-02 14:47:06 -0800216 } else {
Gilad Arnold6dbbd392012-07-10 16:19:11 -0700217 LOG(ERROR) << "Wrote too few bytes: " << bytes_written
Gilad Arnold581c2ea2012-07-19 12:33:49 -0700218 << " < " << buffer_valid_size_[index];
Darin Petkovc2e4a7d2010-12-02 14:47:06 -0800219 }
Gilad Arnold581c2ea2012-07-19 12:33:49 -0700220 failed_ = true;
Darin Petkovc2e4a7d2010-12-02 14:47:06 -0800221 }
222
223 SpawnAsyncActions();
224}
225
226void FilesystemCopierAction::StaticAsyncWriteReadyCallback(
227 GObject *source_object,
228 GAsyncResult *res,
229 gpointer user_data) {
230 reinterpret_cast<FilesystemCopierAction*>(user_data)->
231 AsyncWriteReadyCallback(source_object, res);
232}
233
234void FilesystemCopierAction::SpawnAsyncActions() {
235 bool reading = false;
236 bool writing = false;
237 for (int i = 0; i < 2; i++) {
238 if (buffer_state_[i] == kBufferStateReading) {
239 reading = true;
240 }
241 if (buffer_state_[i] == kBufferStateWriting) {
242 writing = true;
243 }
244 }
245 if (failed_ || cancelled_) {
246 if (!reading && !writing) {
Darin Petkov3aefa862010-12-07 14:45:00 -0800247 Cleanup(kActionCodeError);
Darin Petkovc2e4a7d2010-12-02 14:47:06 -0800248 }
249 return;
250 }
251 for (int i = 0; i < 2; i++) {
252 if (!reading && !read_done_ && buffer_state_[i] == kBufferStateEmpty) {
Darin Petkov3aefa862010-12-07 14:45:00 -0800253 int64_t bytes_to_read = std::min(static_cast<int64_t>(buffer_[0].size()),
254 filesystem_size_);
Darin Petkovc2e4a7d2010-12-02 14:47:06 -0800255 g_input_stream_read_async(
256 src_stream_,
257 buffer_[i].data(),
Darin Petkov3aefa862010-12-07 14:45:00 -0800258 bytes_to_read,
Darin Petkovc2e4a7d2010-12-02 14:47:06 -0800259 G_PRIORITY_DEFAULT,
260 canceller_[i],
261 &FilesystemCopierAction::StaticAsyncReadReadyCallback,
262 this);
263 reading = true;
264 buffer_state_[i] = kBufferStateReading;
Darin Petkov3aefa862010-12-07 14:45:00 -0800265 } else if (!writing && !verify_hash_ &&
266 buffer_state_[i] == kBufferStateFull) {
Darin Petkovc2e4a7d2010-12-02 14:47:06 -0800267 g_output_stream_write_async(
268 dst_stream_,
269 buffer_[i].data(),
270 buffer_valid_size_[i],
271 G_PRIORITY_DEFAULT,
272 canceller_[i],
273 &FilesystemCopierAction::StaticAsyncWriteReadyCallback,
274 this);
275 writing = true;
276 buffer_state_[i] = kBufferStateWriting;
277 }
278 }
279 if (!reading && !writing) {
280 // We're done!
Darin Petkov3aefa862010-12-07 14:45:00 -0800281 ActionExitCode code = kActionCodeSuccess;
Darin Petkovc2e4a7d2010-12-02 14:47:06 -0800282 if (hasher_.Finalize()) {
Darin Petkov3aefa862010-12-07 14:45:00 -0800283 LOG(INFO) << "Hash: " << hasher_.hash();
284 if (verify_hash_) {
285 if (copying_kernel_install_path_) {
286 if (install_plan_.kernel_hash != hasher_.raw_hash()) {
287 code = kActionCodeNewKernelVerificationError;
288 LOG(ERROR) << "New kernel verification failed.";
289 }
290 } else {
291 if (install_plan_.rootfs_hash != hasher_.raw_hash()) {
292 code = kActionCodeNewRootfsVerificationError;
293 LOG(ERROR) << "New rootfs verification failed.";
294 }
295 }
Darin Petkov698d0412010-10-13 10:59:44 -0700296 } else {
Darin Petkov3aefa862010-12-07 14:45:00 -0800297 if (copying_kernel_install_path_) {
298 install_plan_.kernel_hash = hasher_.raw_hash();
299 } else {
300 install_plan_.rootfs_hash = hasher_.raw_hash();
301 }
Darin Petkov698d0412010-10-13 10:59:44 -0700302 }
adlr@google.com3defe6a2009-12-04 20:57:17 +0000303 } else {
Darin Petkovc2e4a7d2010-12-02 14:47:06 -0800304 LOG(ERROR) << "Unable to finalize the hash.";
Darin Petkov3aefa862010-12-07 14:45:00 -0800305 code = kActionCodeError;
adlr@google.com3defe6a2009-12-04 20:57:17 +0000306 }
Darin Petkov3aefa862010-12-07 14:45:00 -0800307 Cleanup(code);
adlr@google.com3defe6a2009-12-04 20:57:17 +0000308 }
Andrew de los Reyesc7020782010-04-28 10:46:04 -0700309}
adlr@google.com3defe6a2009-12-04 20:57:17 +0000310
Darin Petkov698d0412010-10-13 10:59:44 -0700311void FilesystemCopierAction::DetermineFilesystemSize(int fd) {
Darin Petkov3aefa862010-12-07 14:45:00 -0800312 if (verify_hash_) {
313 filesystem_size_ = copying_kernel_install_path_ ?
314 install_plan_.kernel_size : install_plan_.rootfs_size;
315 LOG(INFO) << "Filesystem size: " << filesystem_size_;
316 return;
317 }
Darin Petkov698d0412010-10-13 10:59:44 -0700318 filesystem_size_ = kint64max;
319 int block_count = 0, block_size = 0;
320 if (!copying_kernel_install_path_ &&
321 utils::GetFilesystemSizeFromFD(fd, &block_count, &block_size)) {
322 filesystem_size_ = static_cast<int64_t>(block_count) * block_size;
323 LOG(INFO) << "Filesystem size: " << filesystem_size_ << " bytes ("
324 << block_count << "x" << block_size << ").";
325 }
326}
327
adlr@google.com3defe6a2009-12-04 20:57:17 +0000328} // namespace chromeos_update_engine