blob: 7da54a83cf9a6f792b2e0edbdb34d053b4902e85 [file] [log] [blame]
Darin Petkov9b230572010-10-08 10:20:09 -07001// Copyright (c) 2010 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(
Darin Petkov3aefa862010-12-07 14:45:00 -080039 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),
48 filesystem_size_(kint64max) {
49 // 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();
72
Darin Petkov3aefa862010-12-07 14:45:00 -080073 // Note that we do need to run hash verification for new-style full updates
74 // but currently the |is_full_update| field is set to true only for old-style
75 // full updates and we don't have any expected partition info in that case.
76 if (install_plan_.is_full_update ||
77 (!verify_hash_ && install_plan_.is_resume)) {
78 // No copy or hash verification needed. Done!
Andrew de los Reyesf98bff82010-05-06 13:33:25 -070079 if (HasOutputPipe())
80 SetOutputObject(install_plan_);
Darin Petkovc1a8b422010-07-19 11:34:49 -070081 abort_action_completer.set_code(kActionCodeSuccess);
adlr@google.com3defe6a2009-12-04 20:57:17 +000082 return;
83 }
84
Darin Petkov3aefa862010-12-07 14:45:00 -080085 const string destination = copying_kernel_install_path_ ?
86 install_plan_.kernel_install_path :
87 install_plan_.install_path;
88 string source = verify_hash_ ? destination : copy_source_;
Andrew de los Reyesf9185172010-05-03 11:07:05 -070089 if (source.empty()) {
90 source = copying_kernel_install_path_ ?
91 utils::BootKernelDevice(utils::BootDevice()) :
92 utils::BootDevice();
93 }
Andrew de los Reyesc7020782010-04-28 10:46:04 -070094 int src_fd = open(source.c_str(), O_RDONLY);
95 if (src_fd < 0) {
96 PLOG(ERROR) << "Unable to open " << source << " for reading:";
97 return;
98 }
Darin Petkov3aefa862010-12-07 14:45:00 -080099
100 if (!verify_hash_) {
101 int dst_fd = open(destination.c_str(),
102 O_WRONLY | O_TRUNC | O_CREAT,
Andrew de los Reyesc7020782010-04-28 10:46:04 -0700103 0644);
Darin Petkov3aefa862010-12-07 14:45:00 -0800104 if (dst_fd < 0) {
105 close(src_fd);
106 PLOG(ERROR) << "Unable to open " << install_plan_.install_path
107 << " for writing:";
108 return;
109 }
110
111 dst_stream_ = g_unix_output_stream_new(dst_fd, TRUE);
adlr@google.com3defe6a2009-12-04 20:57:17 +0000112 }
113
Darin Petkov698d0412010-10-13 10:59:44 -0700114 DetermineFilesystemSize(src_fd);
Andrew de los Reyesc7020782010-04-28 10:46:04 -0700115 src_stream_ = g_unix_input_stream_new(src_fd, TRUE);
Andrew de los Reyesc7020782010-04-28 10:46:04 -0700116
Darin Petkovc2e4a7d2010-12-02 14:47:06 -0800117 for (int i = 0; i < 2; i++) {
118 buffer_[i].resize(kCopyFileBufferSize);
119 canceller_[i] = g_cancellable_new();
120 }
Andrew de los Reyesc7020782010-04-28 10:46:04 -0700121
Darin Petkovc2e4a7d2010-12-02 14:47:06 -0800122 // Start the first read.
123 SpawnAsyncActions();
Andrew de los Reyesc7020782010-04-28 10:46:04 -0700124
125 abort_action_completer.set_should_complete(false);
adlr@google.com3defe6a2009-12-04 20:57:17 +0000126}
127
128void FilesystemCopierAction::TerminateProcessing() {
Darin Petkovc2e4a7d2010-12-02 14:47:06 -0800129 for (int i = 0; i < 2; i++) {
130 if (canceller_[i]) {
131 g_cancellable_cancel(canceller_[i]);
132 }
adlr@google.com3defe6a2009-12-04 20:57:17 +0000133 }
134}
135
Darin Petkov3aefa862010-12-07 14:45:00 -0800136void FilesystemCopierAction::Cleanup(ActionExitCode code) {
Darin Petkovc2e4a7d2010-12-02 14:47:06 -0800137 for (int i = 0; i < 2; i++) {
138 g_object_unref(canceller_[i]);
139 canceller_[i] = NULL;
140 }
Andrew de los Reyesc7020782010-04-28 10:46:04 -0700141 g_object_unref(src_stream_);
142 src_stream_ = NULL;
Darin Petkov3aefa862010-12-07 14:45:00 -0800143 if (dst_stream_) {
144 g_object_unref(dst_stream_);
145 dst_stream_ = NULL;
146 }
Darin Petkovc2e4a7d2010-12-02 14:47:06 -0800147 if (cancelled_)
Andrew de los Reyesc7020782010-04-28 10:46:04 -0700148 return;
Darin Petkov3aefa862010-12-07 14:45:00 -0800149 if (code == kActionCodeSuccess && HasOutputPipe())
adlr@google.com3defe6a2009-12-04 20:57:17 +0000150 SetOutputObject(install_plan_);
Darin Petkov3aefa862010-12-07 14:45:00 -0800151 processor_->ActionComplete(this, code);
adlr@google.com3defe6a2009-12-04 20:57:17 +0000152}
153
Darin Petkovc2e4a7d2010-12-02 14:47:06 -0800154void FilesystemCopierAction::AsyncReadReadyCallback(GObject *source_object,
155 GAsyncResult *res) {
156 int index = buffer_state_[0] == kBufferStateReading ? 0 : 1;
157 CHECK(buffer_state_[index] == kBufferStateReading);
158
Andrew de los Reyesc7020782010-04-28 10:46:04 -0700159 GError* error = NULL;
Darin Petkovc2e4a7d2010-12-02 14:47:06 -0800160 CHECK(canceller_[index]);
161 cancelled_ = g_cancellable_is_cancelled(canceller_[index]) == TRUE;
adlr@google.com3defe6a2009-12-04 20:57:17 +0000162
Darin Petkovc2e4a7d2010-12-02 14:47:06 -0800163 ssize_t bytes_read = g_input_stream_read_finish(src_stream_, res, &error);
164 if (bytes_read < 0) {
165 LOG(ERROR) << "Read failed: " << utils::GetGErrorMessage(error);
166 failed_ = true;
167 buffer_state_[index] = kBufferStateEmpty;
168 } else if (bytes_read == 0) {
169 read_done_ = true;
170 buffer_state_[index] = kBufferStateEmpty;
171 } else {
172 buffer_valid_size_[index] = bytes_read;
173 buffer_state_[index] = kBufferStateFull;
Darin Petkovc2e4a7d2010-12-02 14:47:06 -0800174 filesystem_size_ -= bytes_read;
175 }
Darin Petkovc2e4a7d2010-12-02 14:47:06 -0800176 SpawnAsyncActions();
177
178 if (bytes_read > 0) {
Darin Petkov3aefa862010-12-07 14:45:00 -0800179 // If read_done_ is set, SpawnAsyncActions may finalize the hash so the hash
180 // update below would happen too late.
181 CHECK(!read_done_);
Darin Petkovc2e4a7d2010-12-02 14:47:06 -0800182 if (!hasher_.Update(buffer_[index].data(), bytes_read)) {
183 LOG(ERROR) << "Unable to update the hash.";
184 failed_ = true;
adlr@google.com3defe6a2009-12-04 20:57:17 +0000185 }
Darin Petkov3aefa862010-12-07 14:45:00 -0800186 if (verify_hash_) {
187 buffer_state_[index] = kBufferStateEmpty;
188 }
Darin Petkovc2e4a7d2010-12-02 14:47:06 -0800189 }
190}
Andrew de los Reyesc7020782010-04-28 10:46:04 -0700191
Darin Petkovc2e4a7d2010-12-02 14:47:06 -0800192void FilesystemCopierAction::StaticAsyncReadReadyCallback(
193 GObject *source_object,
194 GAsyncResult *res,
195 gpointer user_data) {
196 reinterpret_cast<FilesystemCopierAction*>(user_data)->
197 AsyncReadReadyCallback(source_object, res);
198}
199
200void FilesystemCopierAction::AsyncWriteReadyCallback(GObject *source_object,
201 GAsyncResult *res) {
202 int index = buffer_state_[0] == kBufferStateWriting ? 0 : 1;
203 CHECK(buffer_state_[index] == kBufferStateWriting);
204 buffer_state_[index] = kBufferStateEmpty;
205
206 GError* error = NULL;
207 CHECK(canceller_[index]);
208 cancelled_ = g_cancellable_is_cancelled(canceller_[index]) == TRUE;
209
210 ssize_t bytes_written = g_output_stream_write_finish(dst_stream_,
211 res,
212 &error);
213 if (bytes_written < static_cast<ssize_t>(buffer_valid_size_[index])) {
214 if (bytes_written < 0) {
215 LOG(ERROR) << "Write failed: " << utils::GetGErrorMessage(error);
216 } else {
217 LOG(ERROR) << "Write was short: wrote " << bytes_written
218 << " but expected to write " << buffer_valid_size_[index];
219 }
220 failed_ = true;
221 }
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