blob: 9e434828e0fa38b1d8eb8c800a14187ddac13ff7 [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"
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;
adlr@google.com3defe6a2009-12-04 20:57:17 +000038} // namespace {}
39
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),
Darin Petkovc2e4a7d2010-12-02 14:47:06 -080046 src_stream_(NULL),
47 dst_stream_(NULL),
48 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;
63 canceller_[i] = NULL;
64 }
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();
Gilad Arnold21504f02013-05-24 08:51:22 -070076 if (!verify_hash_ &&
77 (install_plan_.is_resume || install_plan_.is_full_update)) {
Darin Petkov3aefa862010-12-07 14:45:00 -080078 // No copy or hash verification needed. Done!
Gilad Arnold21504f02013-05-24 08:51:22 -070079 LOG(INFO) << "filesystem copying skipped: "
80 << (install_plan_.is_resume ? "resumed" : "full") << " update";
Andrew de los Reyesf98bff82010-05-06 13:33:25 -070081 if (HasOutputPipe())
82 SetOutputObject(install_plan_);
David Zeuthena99981f2013-04-29 13:42:47 -070083 abort_action_completer.set_code(kErrorCodeSuccess);
adlr@google.com3defe6a2009-12-04 20:57:17 +000084 return;
85 }
86
Darin Petkov3aefa862010-12-07 14:45:00 -080087 const string destination = copying_kernel_install_path_ ?
88 install_plan_.kernel_install_path :
89 install_plan_.install_path;
90 string source = verify_hash_ ? destination : copy_source_;
Andrew de los Reyesf9185172010-05-03 11:07:05 -070091 if (source.empty()) {
92 source = copying_kernel_install_path_ ?
J. Richard Barnette30842932013-10-28 15:04:23 -070093 utils::KernelDeviceOfBootDevice(
Alex Deymo42432912013-07-12 20:21:15 -070094 system_state_->hardware()->BootDevice()) :
95 system_state_->hardware()->BootDevice();
Andrew de los Reyesf9185172010-05-03 11:07:05 -070096 }
Andrew de los Reyesc7020782010-04-28 10:46:04 -070097 int src_fd = open(source.c_str(), O_RDONLY);
98 if (src_fd < 0) {
99 PLOG(ERROR) << "Unable to open " << source << " for reading:";
100 return;
101 }
Darin Petkov3aefa862010-12-07 14:45:00 -0800102
103 if (!verify_hash_) {
104 int dst_fd = open(destination.c_str(),
105 O_WRONLY | O_TRUNC | O_CREAT,
Andrew de los Reyesc7020782010-04-28 10:46:04 -0700106 0644);
Darin Petkov3aefa862010-12-07 14:45:00 -0800107 if (dst_fd < 0) {
108 close(src_fd);
109 PLOG(ERROR) << "Unable to open " << install_plan_.install_path
110 << " for writing:";
111 return;
112 }
113
114 dst_stream_ = g_unix_output_stream_new(dst_fd, TRUE);
adlr@google.com3defe6a2009-12-04 20:57:17 +0000115 }
116
Darin Petkov698d0412010-10-13 10:59:44 -0700117 DetermineFilesystemSize(src_fd);
Andrew de los Reyesc7020782010-04-28 10:46:04 -0700118 src_stream_ = g_unix_input_stream_new(src_fd, TRUE);
Andrew de los Reyesc7020782010-04-28 10:46:04 -0700119
Darin Petkovc2e4a7d2010-12-02 14:47:06 -0800120 for (int i = 0; i < 2; i++) {
121 buffer_[i].resize(kCopyFileBufferSize);
122 canceller_[i] = g_cancellable_new();
123 }
Andrew de los Reyesc7020782010-04-28 10:46:04 -0700124
Darin Petkovc2e4a7d2010-12-02 14:47:06 -0800125 // Start the first read.
126 SpawnAsyncActions();
Andrew de los Reyesc7020782010-04-28 10:46:04 -0700127
128 abort_action_completer.set_should_complete(false);
adlr@google.com3defe6a2009-12-04 20:57:17 +0000129}
130
131void FilesystemCopierAction::TerminateProcessing() {
Darin Petkovc2e4a7d2010-12-02 14:47:06 -0800132 for (int i = 0; i < 2; i++) {
133 if (canceller_[i]) {
134 g_cancellable_cancel(canceller_[i]);
135 }
adlr@google.com3defe6a2009-12-04 20:57:17 +0000136 }
137}
138
Ben Chan2add7d72012-10-08 19:28:37 -0700139bool FilesystemCopierAction::IsCleanupPending() const {
140 return (src_stream_ != NULL);
141}
142
David Zeuthena99981f2013-04-29 13:42:47 -0700143void FilesystemCopierAction::Cleanup(ErrorCode code) {
Darin Petkovc2e4a7d2010-12-02 14:47:06 -0800144 for (int i = 0; i < 2; i++) {
145 g_object_unref(canceller_[i]);
146 canceller_[i] = NULL;
147 }
Andrew de los Reyesc7020782010-04-28 10:46:04 -0700148 g_object_unref(src_stream_);
149 src_stream_ = NULL;
Darin Petkov3aefa862010-12-07 14:45:00 -0800150 if (dst_stream_) {
151 g_object_unref(dst_stream_);
152 dst_stream_ = NULL;
153 }
Darin Petkovc2e4a7d2010-12-02 14:47:06 -0800154 if (cancelled_)
Andrew de los Reyesc7020782010-04-28 10:46:04 -0700155 return;
David Zeuthena99981f2013-04-29 13:42:47 -0700156 if (code == kErrorCodeSuccess && HasOutputPipe())
adlr@google.com3defe6a2009-12-04 20:57:17 +0000157 SetOutputObject(install_plan_);
Darin Petkov3aefa862010-12-07 14:45:00 -0800158 processor_->ActionComplete(this, code);
adlr@google.com3defe6a2009-12-04 20:57:17 +0000159}
160
Darin Petkovc2e4a7d2010-12-02 14:47:06 -0800161void FilesystemCopierAction::AsyncReadReadyCallback(GObject *source_object,
162 GAsyncResult *res) {
163 int index = buffer_state_[0] == kBufferStateReading ? 0 : 1;
164 CHECK(buffer_state_[index] == kBufferStateReading);
165
Andrew de los Reyesc7020782010-04-28 10:46:04 -0700166 GError* error = NULL;
Darin Petkovc2e4a7d2010-12-02 14:47:06 -0800167 CHECK(canceller_[index]);
168 cancelled_ = g_cancellable_is_cancelled(canceller_[index]) == TRUE;
adlr@google.com3defe6a2009-12-04 20:57:17 +0000169
Darin Petkovc2e4a7d2010-12-02 14:47:06 -0800170 ssize_t bytes_read = g_input_stream_read_finish(src_stream_, res, &error);
171 if (bytes_read < 0) {
Darin Petkova0b9e772011-10-06 05:05:56 -0700172 LOG(ERROR) << "Read failed: " << utils::GetAndFreeGError(&error);
Darin Petkovc2e4a7d2010-12-02 14:47:06 -0800173 failed_ = true;
174 buffer_state_[index] = kBufferStateEmpty;
175 } else if (bytes_read == 0) {
176 read_done_ = true;
177 buffer_state_[index] = kBufferStateEmpty;
178 } else {
179 buffer_valid_size_[index] = bytes_read;
180 buffer_state_[index] = kBufferStateFull;
Darin Petkovc2e4a7d2010-12-02 14:47:06 -0800181 filesystem_size_ -= bytes_read;
182 }
Darin Petkovc2e4a7d2010-12-02 14:47:06 -0800183 SpawnAsyncActions();
184
185 if (bytes_read > 0) {
Darin Petkov3aefa862010-12-07 14:45:00 -0800186 // If read_done_ is set, SpawnAsyncActions may finalize the hash so the hash
187 // update below would happen too late.
188 CHECK(!read_done_);
Darin Petkovc2e4a7d2010-12-02 14:47:06 -0800189 if (!hasher_.Update(buffer_[index].data(), bytes_read)) {
190 LOG(ERROR) << "Unable to update the hash.";
191 failed_ = true;
adlr@google.com3defe6a2009-12-04 20:57:17 +0000192 }
Darin Petkov3aefa862010-12-07 14:45:00 -0800193 if (verify_hash_) {
194 buffer_state_[index] = kBufferStateEmpty;
195 }
Darin Petkovc2e4a7d2010-12-02 14:47:06 -0800196 }
197}
Andrew de los Reyesc7020782010-04-28 10:46:04 -0700198
Darin Petkovc2e4a7d2010-12-02 14:47:06 -0800199void FilesystemCopierAction::StaticAsyncReadReadyCallback(
200 GObject *source_object,
201 GAsyncResult *res,
202 gpointer user_data) {
203 reinterpret_cast<FilesystemCopierAction*>(user_data)->
204 AsyncReadReadyCallback(source_object, res);
205}
206
207void FilesystemCopierAction::AsyncWriteReadyCallback(GObject *source_object,
208 GAsyncResult *res) {
209 int index = buffer_state_[0] == kBufferStateWriting ? 0 : 1;
210 CHECK(buffer_state_[index] == kBufferStateWriting);
Gilad Arnold581c2ea2012-07-19 12:33:49 -0700211 buffer_state_[index] = kBufferStateEmpty;
Darin Petkovc2e4a7d2010-12-02 14:47:06 -0800212
213 GError* error = NULL;
214 CHECK(canceller_[index]);
215 cancelled_ = g_cancellable_is_cancelled(canceller_[index]) == TRUE;
216
217 ssize_t bytes_written = g_output_stream_write_finish(dst_stream_,
218 res,
219 &error);
Gilad Arnold6dbbd392012-07-10 16:19:11 -0700220
Darin Petkovc2e4a7d2010-12-02 14:47:06 -0800221 if (bytes_written < static_cast<ssize_t>(buffer_valid_size_[index])) {
222 if (bytes_written < 0) {
Gilad Arnold6dbbd392012-07-10 16:19:11 -0700223 LOG(ERROR) << "Write error: " << utils::GetAndFreeGError(&error);
Darin Petkovc2e4a7d2010-12-02 14:47:06 -0800224 } else {
Gilad Arnold6dbbd392012-07-10 16:19:11 -0700225 LOG(ERROR) << "Wrote too few bytes: " << bytes_written
Gilad Arnold581c2ea2012-07-19 12:33:49 -0700226 << " < " << buffer_valid_size_[index];
Darin Petkovc2e4a7d2010-12-02 14:47:06 -0800227 }
Gilad Arnold581c2ea2012-07-19 12:33:49 -0700228 failed_ = true;
Darin Petkovc2e4a7d2010-12-02 14:47:06 -0800229 }
230
231 SpawnAsyncActions();
232}
233
234void FilesystemCopierAction::StaticAsyncWriteReadyCallback(
235 GObject *source_object,
236 GAsyncResult *res,
237 gpointer user_data) {
238 reinterpret_cast<FilesystemCopierAction*>(user_data)->
239 AsyncWriteReadyCallback(source_object, res);
240}
241
242void FilesystemCopierAction::SpawnAsyncActions() {
243 bool reading = false;
244 bool writing = false;
245 for (int i = 0; i < 2; i++) {
246 if (buffer_state_[i] == kBufferStateReading) {
247 reading = true;
248 }
249 if (buffer_state_[i] == kBufferStateWriting) {
250 writing = true;
251 }
252 }
253 if (failed_ || cancelled_) {
254 if (!reading && !writing) {
David Zeuthena99981f2013-04-29 13:42:47 -0700255 Cleanup(kErrorCodeError);
Darin Petkovc2e4a7d2010-12-02 14:47:06 -0800256 }
257 return;
258 }
259 for (int i = 0; i < 2; i++) {
260 if (!reading && !read_done_ && buffer_state_[i] == kBufferStateEmpty) {
Darin Petkov3aefa862010-12-07 14:45:00 -0800261 int64_t bytes_to_read = std::min(static_cast<int64_t>(buffer_[0].size()),
262 filesystem_size_);
Darin Petkovc2e4a7d2010-12-02 14:47:06 -0800263 g_input_stream_read_async(
264 src_stream_,
265 buffer_[i].data(),
Darin Petkov3aefa862010-12-07 14:45:00 -0800266 bytes_to_read,
Darin Petkovc2e4a7d2010-12-02 14:47:06 -0800267 G_PRIORITY_DEFAULT,
268 canceller_[i],
269 &FilesystemCopierAction::StaticAsyncReadReadyCallback,
270 this);
271 reading = true;
272 buffer_state_[i] = kBufferStateReading;
Darin Petkov3aefa862010-12-07 14:45:00 -0800273 } else if (!writing && !verify_hash_ &&
274 buffer_state_[i] == kBufferStateFull) {
Darin Petkovc2e4a7d2010-12-02 14:47:06 -0800275 g_output_stream_write_async(
276 dst_stream_,
277 buffer_[i].data(),
278 buffer_valid_size_[i],
279 G_PRIORITY_DEFAULT,
280 canceller_[i],
281 &FilesystemCopierAction::StaticAsyncWriteReadyCallback,
282 this);
283 writing = true;
284 buffer_state_[i] = kBufferStateWriting;
285 }
286 }
287 if (!reading && !writing) {
288 // We're done!
David Zeuthena99981f2013-04-29 13:42:47 -0700289 ErrorCode code = kErrorCodeSuccess;
Darin Petkovc2e4a7d2010-12-02 14:47:06 -0800290 if (hasher_.Finalize()) {
Darin Petkov3aefa862010-12-07 14:45:00 -0800291 LOG(INFO) << "Hash: " << hasher_.hash();
292 if (verify_hash_) {
293 if (copying_kernel_install_path_) {
294 if (install_plan_.kernel_hash != hasher_.raw_hash()) {
David Zeuthena99981f2013-04-29 13:42:47 -0700295 code = kErrorCodeNewKernelVerificationError;
Darin Petkov3aefa862010-12-07 14:45:00 -0800296 LOG(ERROR) << "New kernel verification failed.";
297 }
298 } else {
299 if (install_plan_.rootfs_hash != hasher_.raw_hash()) {
David Zeuthena99981f2013-04-29 13:42:47 -0700300 code = kErrorCodeNewRootfsVerificationError;
Darin Petkov3aefa862010-12-07 14:45:00 -0800301 LOG(ERROR) << "New rootfs verification failed.";
302 }
303 }
Darin Petkov698d0412010-10-13 10:59:44 -0700304 } else {
Darin Petkov3aefa862010-12-07 14:45:00 -0800305 if (copying_kernel_install_path_) {
306 install_plan_.kernel_hash = hasher_.raw_hash();
307 } else {
308 install_plan_.rootfs_hash = hasher_.raw_hash();
309 }
Darin Petkov698d0412010-10-13 10:59:44 -0700310 }
adlr@google.com3defe6a2009-12-04 20:57:17 +0000311 } else {
Darin Petkovc2e4a7d2010-12-02 14:47:06 -0800312 LOG(ERROR) << "Unable to finalize the hash.";
David Zeuthena99981f2013-04-29 13:42:47 -0700313 code = kErrorCodeError;
adlr@google.com3defe6a2009-12-04 20:57:17 +0000314 }
Darin Petkov3aefa862010-12-07 14:45:00 -0800315 Cleanup(code);
adlr@google.com3defe6a2009-12-04 20:57:17 +0000316 }
Andrew de los Reyesc7020782010-04-28 10:46:04 -0700317}
adlr@google.com3defe6a2009-12-04 20:57:17 +0000318
Darin Petkov698d0412010-10-13 10:59:44 -0700319void FilesystemCopierAction::DetermineFilesystemSize(int fd) {
Darin Petkov3aefa862010-12-07 14:45:00 -0800320 if (verify_hash_) {
321 filesystem_size_ = copying_kernel_install_path_ ?
322 install_plan_.kernel_size : install_plan_.rootfs_size;
323 LOG(INFO) << "Filesystem size: " << filesystem_size_;
324 return;
325 }
Darin Petkov698d0412010-10-13 10:59:44 -0700326 filesystem_size_ = kint64max;
327 int block_count = 0, block_size = 0;
328 if (!copying_kernel_install_path_ &&
329 utils::GetFilesystemSizeFromFD(fd, &block_count, &block_size)) {
330 filesystem_size_ = static_cast<int64_t>(block_count) * block_size;
331 LOG(INFO) << "Filesystem size: " << filesystem_size_ << " bytes ("
332 << block_count << "x" << block_size << ").";
333 }
334}
335
adlr@google.com3defe6a2009-12-04 20:57:17 +0000336} // namespace chromeos_update_engine