blob: 3e5160ed296af8ba509de0457032056ad18f58f3 [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();
Gilad Arnold21504f02013-05-24 08:51:22 -070072 if (!verify_hash_ &&
73 (install_plan_.is_resume || install_plan_.is_full_update)) {
Darin Petkov3aefa862010-12-07 14:45:00 -080074 // No copy or hash verification needed. Done!
Gilad Arnold21504f02013-05-24 08:51:22 -070075 LOG(INFO) << "filesystem copying skipped: "
76 << (install_plan_.is_resume ? "resumed" : "full") << " update";
Andrew de los Reyesf98bff82010-05-06 13:33:25 -070077 if (HasOutputPipe())
78 SetOutputObject(install_plan_);
David Zeuthena99981f2013-04-29 13:42:47 -070079 abort_action_completer.set_code(kErrorCodeSuccess);
adlr@google.com3defe6a2009-12-04 20:57:17 +000080 return;
81 }
82
Darin Petkov3aefa862010-12-07 14:45:00 -080083 const string destination = copying_kernel_install_path_ ?
84 install_plan_.kernel_install_path :
85 install_plan_.install_path;
86 string source = verify_hash_ ? destination : copy_source_;
Andrew de los Reyesf9185172010-05-03 11:07:05 -070087 if (source.empty()) {
88 source = copying_kernel_install_path_ ?
89 utils::BootKernelDevice(utils::BootDevice()) :
90 utils::BootDevice();
91 }
Andrew de los Reyesc7020782010-04-28 10:46:04 -070092 int src_fd = open(source.c_str(), O_RDONLY);
93 if (src_fd < 0) {
94 PLOG(ERROR) << "Unable to open " << source << " for reading:";
95 return;
96 }
Darin Petkov3aefa862010-12-07 14:45:00 -080097
98 if (!verify_hash_) {
99 int dst_fd = open(destination.c_str(),
100 O_WRONLY | O_TRUNC | O_CREAT,
Andrew de los Reyesc7020782010-04-28 10:46:04 -0700101 0644);
Darin Petkov3aefa862010-12-07 14:45:00 -0800102 if (dst_fd < 0) {
103 close(src_fd);
104 PLOG(ERROR) << "Unable to open " << install_plan_.install_path
105 << " for writing:";
106 return;
107 }
108
109 dst_stream_ = g_unix_output_stream_new(dst_fd, TRUE);
adlr@google.com3defe6a2009-12-04 20:57:17 +0000110 }
111
Darin Petkov698d0412010-10-13 10:59:44 -0700112 DetermineFilesystemSize(src_fd);
Andrew de los Reyesc7020782010-04-28 10:46:04 -0700113 src_stream_ = g_unix_input_stream_new(src_fd, TRUE);
Andrew de los Reyesc7020782010-04-28 10:46:04 -0700114
Darin Petkovc2e4a7d2010-12-02 14:47:06 -0800115 for (int i = 0; i < 2; i++) {
116 buffer_[i].resize(kCopyFileBufferSize);
117 canceller_[i] = g_cancellable_new();
118 }
Andrew de los Reyesc7020782010-04-28 10:46:04 -0700119
Darin Petkovc2e4a7d2010-12-02 14:47:06 -0800120 // Start the first read.
121 SpawnAsyncActions();
Andrew de los Reyesc7020782010-04-28 10:46:04 -0700122
123 abort_action_completer.set_should_complete(false);
adlr@google.com3defe6a2009-12-04 20:57:17 +0000124}
125
126void FilesystemCopierAction::TerminateProcessing() {
Darin Petkovc2e4a7d2010-12-02 14:47:06 -0800127 for (int i = 0; i < 2; i++) {
128 if (canceller_[i]) {
129 g_cancellable_cancel(canceller_[i]);
130 }
adlr@google.com3defe6a2009-12-04 20:57:17 +0000131 }
132}
133
Ben Chan2add7d72012-10-08 19:28:37 -0700134bool FilesystemCopierAction::IsCleanupPending() const {
135 return (src_stream_ != NULL);
136}
137
David Zeuthena99981f2013-04-29 13:42:47 -0700138void FilesystemCopierAction::Cleanup(ErrorCode code) {
Darin Petkovc2e4a7d2010-12-02 14:47:06 -0800139 for (int i = 0; i < 2; i++) {
140 g_object_unref(canceller_[i]);
141 canceller_[i] = NULL;
142 }
Andrew de los Reyesc7020782010-04-28 10:46:04 -0700143 g_object_unref(src_stream_);
144 src_stream_ = NULL;
Darin Petkov3aefa862010-12-07 14:45:00 -0800145 if (dst_stream_) {
146 g_object_unref(dst_stream_);
147 dst_stream_ = NULL;
148 }
Darin Petkovc2e4a7d2010-12-02 14:47:06 -0800149 if (cancelled_)
Andrew de los Reyesc7020782010-04-28 10:46:04 -0700150 return;
David Zeuthena99981f2013-04-29 13:42:47 -0700151 if (code == kErrorCodeSuccess && HasOutputPipe())
adlr@google.com3defe6a2009-12-04 20:57:17 +0000152 SetOutputObject(install_plan_);
Darin Petkov3aefa862010-12-07 14:45:00 -0800153 processor_->ActionComplete(this, code);
adlr@google.com3defe6a2009-12-04 20:57:17 +0000154}
155
Darin Petkovc2e4a7d2010-12-02 14:47:06 -0800156void FilesystemCopierAction::AsyncReadReadyCallback(GObject *source_object,
157 GAsyncResult *res) {
158 int index = buffer_state_[0] == kBufferStateReading ? 0 : 1;
159 CHECK(buffer_state_[index] == kBufferStateReading);
160
Andrew de los Reyesc7020782010-04-28 10:46:04 -0700161 GError* error = NULL;
Darin Petkovc2e4a7d2010-12-02 14:47:06 -0800162 CHECK(canceller_[index]);
163 cancelled_ = g_cancellable_is_cancelled(canceller_[index]) == TRUE;
adlr@google.com3defe6a2009-12-04 20:57:17 +0000164
Darin Petkovc2e4a7d2010-12-02 14:47:06 -0800165 ssize_t bytes_read = g_input_stream_read_finish(src_stream_, res, &error);
166 if (bytes_read < 0) {
Darin Petkova0b9e772011-10-06 05:05:56 -0700167 LOG(ERROR) << "Read failed: " << utils::GetAndFreeGError(&error);
Darin Petkovc2e4a7d2010-12-02 14:47:06 -0800168 failed_ = true;
169 buffer_state_[index] = kBufferStateEmpty;
170 } else if (bytes_read == 0) {
171 read_done_ = true;
172 buffer_state_[index] = kBufferStateEmpty;
173 } else {
174 buffer_valid_size_[index] = bytes_read;
175 buffer_state_[index] = kBufferStateFull;
Darin Petkovc2e4a7d2010-12-02 14:47:06 -0800176 filesystem_size_ -= bytes_read;
177 }
Darin Petkovc2e4a7d2010-12-02 14:47:06 -0800178 SpawnAsyncActions();
179
180 if (bytes_read > 0) {
Darin Petkov3aefa862010-12-07 14:45:00 -0800181 // If read_done_ is set, SpawnAsyncActions may finalize the hash so the hash
182 // update below would happen too late.
183 CHECK(!read_done_);
Darin Petkovc2e4a7d2010-12-02 14:47:06 -0800184 if (!hasher_.Update(buffer_[index].data(), bytes_read)) {
185 LOG(ERROR) << "Unable to update the hash.";
186 failed_ = true;
adlr@google.com3defe6a2009-12-04 20:57:17 +0000187 }
Darin Petkov3aefa862010-12-07 14:45:00 -0800188 if (verify_hash_) {
189 buffer_state_[index] = kBufferStateEmpty;
190 }
Darin Petkovc2e4a7d2010-12-02 14:47:06 -0800191 }
192}
Andrew de los Reyesc7020782010-04-28 10:46:04 -0700193
Darin Petkovc2e4a7d2010-12-02 14:47:06 -0800194void FilesystemCopierAction::StaticAsyncReadReadyCallback(
195 GObject *source_object,
196 GAsyncResult *res,
197 gpointer user_data) {
198 reinterpret_cast<FilesystemCopierAction*>(user_data)->
199 AsyncReadReadyCallback(source_object, res);
200}
201
202void FilesystemCopierAction::AsyncWriteReadyCallback(GObject *source_object,
203 GAsyncResult *res) {
204 int index = buffer_state_[0] == kBufferStateWriting ? 0 : 1;
205 CHECK(buffer_state_[index] == kBufferStateWriting);
Gilad Arnold581c2ea2012-07-19 12:33:49 -0700206 buffer_state_[index] = kBufferStateEmpty;
Darin Petkovc2e4a7d2010-12-02 14:47:06 -0800207
208 GError* error = NULL;
209 CHECK(canceller_[index]);
210 cancelled_ = g_cancellable_is_cancelled(canceller_[index]) == TRUE;
211
212 ssize_t bytes_written = g_output_stream_write_finish(dst_stream_,
213 res,
214 &error);
Gilad Arnold6dbbd392012-07-10 16:19:11 -0700215
Darin Petkovc2e4a7d2010-12-02 14:47:06 -0800216 if (bytes_written < static_cast<ssize_t>(buffer_valid_size_[index])) {
217 if (bytes_written < 0) {
Gilad Arnold6dbbd392012-07-10 16:19:11 -0700218 LOG(ERROR) << "Write error: " << utils::GetAndFreeGError(&error);
Darin Petkovc2e4a7d2010-12-02 14:47:06 -0800219 } else {
Gilad Arnold6dbbd392012-07-10 16:19:11 -0700220 LOG(ERROR) << "Wrote too few bytes: " << bytes_written
Gilad Arnold581c2ea2012-07-19 12:33:49 -0700221 << " < " << buffer_valid_size_[index];
Darin Petkovc2e4a7d2010-12-02 14:47:06 -0800222 }
Gilad Arnold581c2ea2012-07-19 12:33:49 -0700223 failed_ = true;
Darin Petkovc2e4a7d2010-12-02 14:47:06 -0800224 }
225
226 SpawnAsyncActions();
227}
228
229void FilesystemCopierAction::StaticAsyncWriteReadyCallback(
230 GObject *source_object,
231 GAsyncResult *res,
232 gpointer user_data) {
233 reinterpret_cast<FilesystemCopierAction*>(user_data)->
234 AsyncWriteReadyCallback(source_object, res);
235}
236
237void FilesystemCopierAction::SpawnAsyncActions() {
238 bool reading = false;
239 bool writing = false;
240 for (int i = 0; i < 2; i++) {
241 if (buffer_state_[i] == kBufferStateReading) {
242 reading = true;
243 }
244 if (buffer_state_[i] == kBufferStateWriting) {
245 writing = true;
246 }
247 }
248 if (failed_ || cancelled_) {
249 if (!reading && !writing) {
David Zeuthena99981f2013-04-29 13:42:47 -0700250 Cleanup(kErrorCodeError);
Darin Petkovc2e4a7d2010-12-02 14:47:06 -0800251 }
252 return;
253 }
254 for (int i = 0; i < 2; i++) {
255 if (!reading && !read_done_ && buffer_state_[i] == kBufferStateEmpty) {
Darin Petkov3aefa862010-12-07 14:45:00 -0800256 int64_t bytes_to_read = std::min(static_cast<int64_t>(buffer_[0].size()),
257 filesystem_size_);
Darin Petkovc2e4a7d2010-12-02 14:47:06 -0800258 g_input_stream_read_async(
259 src_stream_,
260 buffer_[i].data(),
Darin Petkov3aefa862010-12-07 14:45:00 -0800261 bytes_to_read,
Darin Petkovc2e4a7d2010-12-02 14:47:06 -0800262 G_PRIORITY_DEFAULT,
263 canceller_[i],
264 &FilesystemCopierAction::StaticAsyncReadReadyCallback,
265 this);
266 reading = true;
267 buffer_state_[i] = kBufferStateReading;
Darin Petkov3aefa862010-12-07 14:45:00 -0800268 } else if (!writing && !verify_hash_ &&
269 buffer_state_[i] == kBufferStateFull) {
Darin Petkovc2e4a7d2010-12-02 14:47:06 -0800270 g_output_stream_write_async(
271 dst_stream_,
272 buffer_[i].data(),
273 buffer_valid_size_[i],
274 G_PRIORITY_DEFAULT,
275 canceller_[i],
276 &FilesystemCopierAction::StaticAsyncWriteReadyCallback,
277 this);
278 writing = true;
279 buffer_state_[i] = kBufferStateWriting;
280 }
281 }
282 if (!reading && !writing) {
283 // We're done!
David Zeuthena99981f2013-04-29 13:42:47 -0700284 ErrorCode code = kErrorCodeSuccess;
Darin Petkovc2e4a7d2010-12-02 14:47:06 -0800285 if (hasher_.Finalize()) {
Darin Petkov3aefa862010-12-07 14:45:00 -0800286 LOG(INFO) << "Hash: " << hasher_.hash();
287 if (verify_hash_) {
288 if (copying_kernel_install_path_) {
289 if (install_plan_.kernel_hash != hasher_.raw_hash()) {
David Zeuthena99981f2013-04-29 13:42:47 -0700290 code = kErrorCodeNewKernelVerificationError;
Darin Petkov3aefa862010-12-07 14:45:00 -0800291 LOG(ERROR) << "New kernel verification failed.";
292 }
293 } else {
294 if (install_plan_.rootfs_hash != hasher_.raw_hash()) {
David Zeuthena99981f2013-04-29 13:42:47 -0700295 code = kErrorCodeNewRootfsVerificationError;
Darin Petkov3aefa862010-12-07 14:45:00 -0800296 LOG(ERROR) << "New rootfs verification failed.";
297 }
298 }
Darin Petkov698d0412010-10-13 10:59:44 -0700299 } else {
Darin Petkov3aefa862010-12-07 14:45:00 -0800300 if (copying_kernel_install_path_) {
301 install_plan_.kernel_hash = hasher_.raw_hash();
302 } else {
303 install_plan_.rootfs_hash = hasher_.raw_hash();
304 }
Darin Petkov698d0412010-10-13 10:59:44 -0700305 }
adlr@google.com3defe6a2009-12-04 20:57:17 +0000306 } else {
Darin Petkovc2e4a7d2010-12-02 14:47:06 -0800307 LOG(ERROR) << "Unable to finalize the hash.";
David Zeuthena99981f2013-04-29 13:42:47 -0700308 code = kErrorCodeError;
adlr@google.com3defe6a2009-12-04 20:57:17 +0000309 }
Darin Petkov3aefa862010-12-07 14:45:00 -0800310 Cleanup(code);
adlr@google.com3defe6a2009-12-04 20:57:17 +0000311 }
Andrew de los Reyesc7020782010-04-28 10:46:04 -0700312}
adlr@google.com3defe6a2009-12-04 20:57:17 +0000313
Darin Petkov698d0412010-10-13 10:59:44 -0700314void FilesystemCopierAction::DetermineFilesystemSize(int fd) {
Darin Petkov3aefa862010-12-07 14:45:00 -0800315 if (verify_hash_) {
316 filesystem_size_ = copying_kernel_install_path_ ?
317 install_plan_.kernel_size : install_plan_.rootfs_size;
318 LOG(INFO) << "Filesystem size: " << filesystem_size_;
319 return;
320 }
Darin Petkov698d0412010-10-13 10:59:44 -0700321 filesystem_size_ = kint64max;
322 int block_count = 0, block_size = 0;
323 if (!copying_kernel_install_path_ &&
324 utils::GetFilesystemSizeFromFD(fd, &block_count, &block_size)) {
325 filesystem_size_ = static_cast<int64_t>(block_count) * block_size;
326 LOG(INFO) << "Filesystem size: " << filesystem_size_ << " bytes ("
327 << block_count << "x" << block_size << ").";
328 }
329}
330
adlr@google.com3defe6a2009-12-04 20:57:17 +0000331} // namespace chromeos_update_engine