blob: 1f0fbc7002942273a45037d8ecf9b88534388d01 [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
Gilad Arnold6dbbd392012-07-10 16:19:11 -070018#include <base/string_util.h>
19#include <base/stringprintf.h>
Andrew de los Reyesc7020782010-04-28 10:46:04 -070020#include <gio/gio.h>
21#include <gio/gunixinputstream.h>
22#include <gio/gunixoutputstream.h>
23#include <glib.h>
Darin Petkov9b230572010-10-08 10:20:09 -070024
adlr@google.com3defe6a2009-12-04 20:57:17 +000025#include "update_engine/filesystem_iterator.h"
26#include "update_engine/subprocess.h"
27#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(
Gilad Arnold6dbbd392012-07-10 16:19:11 -070041 bool copying_kernel_install_path, bool verify_hash,
42 unsigned max_rewrite_attempts)
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),
Gilad Arnold6dbbd392012-07-10 16:19:11 -070050 filesystem_size_(kint64max),
51 total_bytes_written_(0),
52 num_curr_rewrite_attempts_(0),
53 num_total_rewrite_attempts_(0),
54 max_rewrite_attempts_(max_rewrite_attempts),
55 is_debug_last_read_(false) {
Darin Petkovc2e4a7d2010-12-02 14:47:06 -080056 // A lot of code works on the implicit assumption that processing is done on
57 // exactly 2 ping-pong buffers.
58 COMPILE_ASSERT(arraysize(buffer_) == 2 &&
59 arraysize(buffer_state_) == 2 &&
60 arraysize(buffer_valid_size_) == 2 &&
61 arraysize(canceller_) == 2,
62 ping_pong_buffers_not_two);
63 for (int i = 0; i < 2; ++i) {
64 buffer_state_[i] = kBufferStateEmpty;
65 buffer_valid_size_[i] = 0;
66 canceller_[i] = NULL;
67 }
68}
69
adlr@google.com3defe6a2009-12-04 20:57:17 +000070void FilesystemCopierAction::PerformAction() {
Andrew de los Reyesc7020782010-04-28 10:46:04 -070071 // Will tell the ActionProcessor we've failed if we return.
72 ScopedActionCompleter abort_action_completer(processor_, this);
73
adlr@google.com3defe6a2009-12-04 20:57:17 +000074 if (!HasInputObject()) {
Andrew de los Reyesc7020782010-04-28 10:46:04 -070075 LOG(ERROR) << "FilesystemCopierAction missing input object.";
adlr@google.com3defe6a2009-12-04 20:57:17 +000076 return;
77 }
78 install_plan_ = GetInputObject();
Darin Petkov7ed561b2011-10-04 02:59:03 -070079 if (!verify_hash_ && install_plan_.is_resume) {
Darin Petkov3aefa862010-12-07 14:45:00 -080080 // No copy or hash verification needed. Done!
Andrew de los Reyesf98bff82010-05-06 13:33:25 -070081 if (HasOutputPipe())
82 SetOutputObject(install_plan_);
Darin Petkovc1a8b422010-07-19 11:34:49 -070083 abort_action_completer.set_code(kActionCodeSuccess);
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_ ?
93 utils::BootKernelDevice(utils::BootDevice()) :
94 utils::BootDevice();
95 }
Andrew de los Reyesc7020782010-04-28 10:46:04 -070096 int src_fd = open(source.c_str(), O_RDONLY);
97 if (src_fd < 0) {
98 PLOG(ERROR) << "Unable to open " << source << " for reading:";
99 return;
100 }
Darin Petkov3aefa862010-12-07 14:45:00 -0800101
102 if (!verify_hash_) {
103 int dst_fd = open(destination.c_str(),
104 O_WRONLY | O_TRUNC | O_CREAT,
Andrew de los Reyesc7020782010-04-28 10:46:04 -0700105 0644);
Darin Petkov3aefa862010-12-07 14:45:00 -0800106 if (dst_fd < 0) {
107 close(src_fd);
108 PLOG(ERROR) << "Unable to open " << install_plan_.install_path
109 << " for writing:";
110 return;
111 }
112
113 dst_stream_ = g_unix_output_stream_new(dst_fd, TRUE);
adlr@google.com3defe6a2009-12-04 20:57:17 +0000114 }
115
Darin Petkov698d0412010-10-13 10:59:44 -0700116 DetermineFilesystemSize(src_fd);
Andrew de los Reyesc7020782010-04-28 10:46:04 -0700117 src_stream_ = g_unix_input_stream_new(src_fd, TRUE);
Andrew de los Reyesc7020782010-04-28 10:46:04 -0700118
Darin Petkovc2e4a7d2010-12-02 14:47:06 -0800119 for (int i = 0; i < 2; i++) {
120 buffer_[i].resize(kCopyFileBufferSize);
121 canceller_[i] = g_cancellable_new();
122 }
Andrew de los Reyesc7020782010-04-28 10:46:04 -0700123
Darin Petkovc2e4a7d2010-12-02 14:47:06 -0800124 // Start the first read.
125 SpawnAsyncActions();
Andrew de los Reyesc7020782010-04-28 10:46:04 -0700126
127 abort_action_completer.set_should_complete(false);
adlr@google.com3defe6a2009-12-04 20:57:17 +0000128}
129
130void FilesystemCopierAction::TerminateProcessing() {
Darin Petkovc2e4a7d2010-12-02 14:47:06 -0800131 for (int i = 0; i < 2; i++) {
132 if (canceller_[i]) {
133 g_cancellable_cancel(canceller_[i]);
134 }
adlr@google.com3defe6a2009-12-04 20:57:17 +0000135 }
136}
137
Darin Petkov3aefa862010-12-07 14:45:00 -0800138void FilesystemCopierAction::Cleanup(ActionExitCode 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;
Darin Petkov3aefa862010-12-07 14:45:00 -0800151 if (code == kActionCodeSuccess && 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);
Gilad Arnold6dbbd392012-07-10 16:19:11 -0700166
167 // TODO(garnold) this is debugging code which needs to be removed once we
168 // figure out the reasons for write I/O error.
169 ssize_t bytes_expected = std::min(static_cast<int64_t>(buffer_[0].size()),
170 filesystem_size_);
171 if (!is_debug_last_read_ && bytes_read > 0 && bytes_read < bytes_expected) {
172 LOG(INFO) << "[debug] read fewer bytes than expected ("
173 << bytes_read << " < " << bytes_expected
174 << "), this is likely the final read";
175 is_debug_last_read_ = true;
176 }
177
Darin Petkovc2e4a7d2010-12-02 14:47:06 -0800178 if (bytes_read < 0) {
Darin Petkova0b9e772011-10-06 05:05:56 -0700179 LOG(ERROR) << "Read failed: " << utils::GetAndFreeGError(&error);
Darin Petkovc2e4a7d2010-12-02 14:47:06 -0800180 failed_ = true;
181 buffer_state_[index] = kBufferStateEmpty;
182 } else if (bytes_read == 0) {
183 read_done_ = true;
184 buffer_state_[index] = kBufferStateEmpty;
185 } else {
186 buffer_valid_size_[index] = bytes_read;
187 buffer_state_[index] = kBufferStateFull;
Darin Petkovc2e4a7d2010-12-02 14:47:06 -0800188 filesystem_size_ -= bytes_read;
189 }
Darin Petkovc2e4a7d2010-12-02 14:47:06 -0800190 SpawnAsyncActions();
191
192 if (bytes_read > 0) {
Darin Petkov3aefa862010-12-07 14:45:00 -0800193 // If read_done_ is set, SpawnAsyncActions may finalize the hash so the hash
194 // update below would happen too late.
195 CHECK(!read_done_);
Darin Petkovc2e4a7d2010-12-02 14:47:06 -0800196 if (!hasher_.Update(buffer_[index].data(), bytes_read)) {
197 LOG(ERROR) << "Unable to update the hash.";
198 failed_ = true;
adlr@google.com3defe6a2009-12-04 20:57:17 +0000199 }
Darin Petkov3aefa862010-12-07 14:45:00 -0800200 if (verify_hash_) {
201 buffer_state_[index] = kBufferStateEmpty;
202 }
Darin Petkovc2e4a7d2010-12-02 14:47:06 -0800203 }
204}
Andrew de los Reyesc7020782010-04-28 10:46:04 -0700205
Darin Petkovc2e4a7d2010-12-02 14:47:06 -0800206void FilesystemCopierAction::StaticAsyncReadReadyCallback(
207 GObject *source_object,
208 GAsyncResult *res,
209 gpointer user_data) {
210 reinterpret_cast<FilesystemCopierAction*>(user_data)->
211 AsyncReadReadyCallback(source_object, res);
212}
213
214void FilesystemCopierAction::AsyncWriteReadyCallback(GObject *source_object,
215 GAsyncResult *res) {
216 int index = buffer_state_[0] == kBufferStateWriting ? 0 : 1;
217 CHECK(buffer_state_[index] == kBufferStateWriting);
Darin Petkovc2e4a7d2010-12-02 14:47:06 -0800218
219 GError* error = NULL;
220 CHECK(canceller_[index]);
221 cancelled_ = g_cancellable_is_cancelled(canceller_[index]) == TRUE;
222
223 ssize_t bytes_written = g_output_stream_write_finish(dst_stream_,
224 res,
225 &error);
Gilad Arnold6dbbd392012-07-10 16:19:11 -0700226
Darin Petkovc2e4a7d2010-12-02 14:47:06 -0800227 if (bytes_written < static_cast<ssize_t>(buffer_valid_size_[index])) {
228 if (bytes_written < 0) {
Gilad Arnold6dbbd392012-07-10 16:19:11 -0700229 LOG(ERROR) << "Write error: " << utils::GetAndFreeGError(&error);
Darin Petkovc2e4a7d2010-12-02 14:47:06 -0800230 } else {
Gilad Arnold6dbbd392012-07-10 16:19:11 -0700231 LOG(ERROR) << "Wrote too few bytes: " << bytes_written
232 << " instead of " << buffer_valid_size_[index];
Darin Petkovc2e4a7d2010-12-02 14:47:06 -0800233 }
Gilad Arnold6dbbd392012-07-10 16:19:11 -0700234
235 bool do_fail = true; // fail, unless we can retry
236
237 if (cancelled_) {
238 LOG(ERROR) << "write operation cancelled, copying failed";
239 } else if (num_curr_rewrite_attempts_ < max_rewrite_attempts_) {
240 // Try again: mark buffer as full, reposition the output stream.
241 num_curr_rewrite_attempts_++;
242 num_total_rewrite_attempts_++;
243 off_t ret =
244 lseek(g_unix_output_stream_get_fd(G_UNIX_OUTPUT_STREAM(dst_stream_)),
245 static_cast<off_t>(total_bytes_written_),
246 SEEK_SET);
247 if (ret == static_cast<off_t>(total_bytes_written_)) {
248 buffer_state_[index] = kBufferStateFull;
249 do_fail = false;
250 LOG(INFO) << "attempting to rewrite current buffer at offset "
251 << total_bytes_written_ << " (" << num_curr_rewrite_attempts_
252 << " of " << max_rewrite_attempts_ << " attempts)";
253 } else {
254 PLOG(ERROR)
255 << "can't reposition output stream for rewrite, copying failed";
256 }
257 } else {
258 LOG(ERROR) << "rewrite attempts exhausted (" << max_rewrite_attempts_
259 << "), copying failed";
260 }
261
262 if (do_fail)
263 failed_ = true;
264 } else {
265 total_bytes_written_ += static_cast<size_t>(bytes_written);
266 num_curr_rewrite_attempts_ = 0;
Darin Petkovc2e4a7d2010-12-02 14:47:06 -0800267 }
268
Gilad Arnold6dbbd392012-07-10 16:19:11 -0700269 if (buffer_state_[index] == kBufferStateWriting)
270 buffer_state_[index] = kBufferStateEmpty;
271
Darin Petkovc2e4a7d2010-12-02 14:47:06 -0800272 SpawnAsyncActions();
273}
274
275void FilesystemCopierAction::StaticAsyncWriteReadyCallback(
276 GObject *source_object,
277 GAsyncResult *res,
278 gpointer user_data) {
279 reinterpret_cast<FilesystemCopierAction*>(user_data)->
280 AsyncWriteReadyCallback(source_object, res);
281}
282
283void FilesystemCopierAction::SpawnAsyncActions() {
284 bool reading = false;
285 bool writing = false;
286 for (int i = 0; i < 2; i++) {
287 if (buffer_state_[i] == kBufferStateReading) {
288 reading = true;
289 }
290 if (buffer_state_[i] == kBufferStateWriting) {
291 writing = true;
292 }
293 }
294 if (failed_ || cancelled_) {
295 if (!reading && !writing) {
Darin Petkov3aefa862010-12-07 14:45:00 -0800296 Cleanup(kActionCodeError);
Darin Petkovc2e4a7d2010-12-02 14:47:06 -0800297 }
298 return;
299 }
300 for (int i = 0; i < 2; i++) {
301 if (!reading && !read_done_ && buffer_state_[i] == kBufferStateEmpty) {
Darin Petkov3aefa862010-12-07 14:45:00 -0800302 int64_t bytes_to_read = std::min(static_cast<int64_t>(buffer_[0].size()),
303 filesystem_size_);
Darin Petkovc2e4a7d2010-12-02 14:47:06 -0800304 g_input_stream_read_async(
305 src_stream_,
306 buffer_[i].data(),
Darin Petkov3aefa862010-12-07 14:45:00 -0800307 bytes_to_read,
Darin Petkovc2e4a7d2010-12-02 14:47:06 -0800308 G_PRIORITY_DEFAULT,
309 canceller_[i],
310 &FilesystemCopierAction::StaticAsyncReadReadyCallback,
311 this);
312 reading = true;
313 buffer_state_[i] = kBufferStateReading;
Darin Petkov3aefa862010-12-07 14:45:00 -0800314 } else if (!writing && !verify_hash_ &&
315 buffer_state_[i] == kBufferStateFull) {
Gilad Arnold6dbbd392012-07-10 16:19:11 -0700316 // TODO(garnold) debugging code, to be removed.
317 if (is_debug_last_read_) {
318 LOG(INFO) << "[debug] spawning async write of "
319 << buffer_valid_size_[i] << " bytes";
320 }
321
Darin Petkovc2e4a7d2010-12-02 14:47:06 -0800322 g_output_stream_write_async(
323 dst_stream_,
324 buffer_[i].data(),
325 buffer_valid_size_[i],
326 G_PRIORITY_DEFAULT,
327 canceller_[i],
328 &FilesystemCopierAction::StaticAsyncWriteReadyCallback,
329 this);
330 writing = true;
331 buffer_state_[i] = kBufferStateWriting;
332 }
333 }
334 if (!reading && !writing) {
335 // We're done!
Darin Petkov3aefa862010-12-07 14:45:00 -0800336 ActionExitCode code = kActionCodeSuccess;
Gilad Arnold6dbbd392012-07-10 16:19:11 -0700337 // TODO(garnold) we're signaling a failure if buffer rewriting was attempted
338 // during the copy action; this is necessary for keeping our unit tests
339 // failing, allowing us to diagnose the root cause for write I/O errors.
340 // When diagnosis is complete, this error return code needs to be reversed.
341 if (num_total_rewrite_attempts_) {
342 code = kActionCodeError;
343 LOG(ERROR) << "[debug] rewrite was attempted "
344 << num_total_rewrite_attempts_ << " time(s), copying failed";
345 }
Darin Petkovc2e4a7d2010-12-02 14:47:06 -0800346 if (hasher_.Finalize()) {
Darin Petkov3aefa862010-12-07 14:45:00 -0800347 LOG(INFO) << "Hash: " << hasher_.hash();
348 if (verify_hash_) {
349 if (copying_kernel_install_path_) {
350 if (install_plan_.kernel_hash != hasher_.raw_hash()) {
351 code = kActionCodeNewKernelVerificationError;
352 LOG(ERROR) << "New kernel verification failed.";
353 }
354 } else {
355 if (install_plan_.rootfs_hash != hasher_.raw_hash()) {
356 code = kActionCodeNewRootfsVerificationError;
357 LOG(ERROR) << "New rootfs verification failed.";
358 }
359 }
Darin Petkov698d0412010-10-13 10:59:44 -0700360 } else {
Darin Petkov3aefa862010-12-07 14:45:00 -0800361 if (copying_kernel_install_path_) {
362 install_plan_.kernel_hash = hasher_.raw_hash();
363 } else {
364 install_plan_.rootfs_hash = hasher_.raw_hash();
365 }
Darin Petkov698d0412010-10-13 10:59:44 -0700366 }
adlr@google.com3defe6a2009-12-04 20:57:17 +0000367 } else {
Darin Petkovc2e4a7d2010-12-02 14:47:06 -0800368 LOG(ERROR) << "Unable to finalize the hash.";
Darin Petkov3aefa862010-12-07 14:45:00 -0800369 code = kActionCodeError;
adlr@google.com3defe6a2009-12-04 20:57:17 +0000370 }
Darin Petkov3aefa862010-12-07 14:45:00 -0800371 Cleanup(code);
adlr@google.com3defe6a2009-12-04 20:57:17 +0000372 }
Andrew de los Reyesc7020782010-04-28 10:46:04 -0700373}
adlr@google.com3defe6a2009-12-04 20:57:17 +0000374
Darin Petkov698d0412010-10-13 10:59:44 -0700375void FilesystemCopierAction::DetermineFilesystemSize(int fd) {
Darin Petkov3aefa862010-12-07 14:45:00 -0800376 if (verify_hash_) {
377 filesystem_size_ = copying_kernel_install_path_ ?
378 install_plan_.kernel_size : install_plan_.rootfs_size;
379 LOG(INFO) << "Filesystem size: " << filesystem_size_;
380 return;
381 }
Darin Petkov698d0412010-10-13 10:59:44 -0700382 filesystem_size_ = kint64max;
383 int block_count = 0, block_size = 0;
384 if (!copying_kernel_install_path_ &&
385 utils::GetFilesystemSizeFromFD(fd, &block_count, &block_size)) {
386 filesystem_size_ = static_cast<int64_t>(block_count) * block_size;
387 LOG(INFO) << "Filesystem size: " << filesystem_size_ << " bytes ("
388 << block_count << "x" << block_size << ").";
389 }
390}
391
adlr@google.com3defe6a2009-12-04 20:57:17 +0000392} // namespace chromeos_update_engine