blob: f3f71bdbee4b107ddfc7ccba92e1e68cb168da91 [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 Petkovc2e4a7d2010-12-02 14:47:06 -080035const off_t kCopyFileBufferSize = 512 * 1024;
adlr@google.com3defe6a2009-12-04 20:57:17 +000036} // namespace {}
37
Darin Petkovc2e4a7d2010-12-02 14:47:06 -080038FilesystemCopierAction::FilesystemCopierAction(
39 bool copying_kernel_install_path)
40 : copying_kernel_install_path_(copying_kernel_install_path),
41 src_stream_(NULL),
42 dst_stream_(NULL),
43 read_done_(false),
44 failed_(false),
45 cancelled_(false),
46 filesystem_size_(kint64max) {
47 // A lot of code works on the implicit assumption that processing is done on
48 // exactly 2 ping-pong buffers.
49 COMPILE_ASSERT(arraysize(buffer_) == 2 &&
50 arraysize(buffer_state_) == 2 &&
51 arraysize(buffer_valid_size_) == 2 &&
52 arraysize(canceller_) == 2,
53 ping_pong_buffers_not_two);
54 for (int i = 0; i < 2; ++i) {
55 buffer_state_[i] = kBufferStateEmpty;
56 buffer_valid_size_[i] = 0;
57 canceller_[i] = NULL;
58 }
59}
60
adlr@google.com3defe6a2009-12-04 20:57:17 +000061void FilesystemCopierAction::PerformAction() {
Andrew de los Reyesc7020782010-04-28 10:46:04 -070062 // Will tell the ActionProcessor we've failed if we return.
63 ScopedActionCompleter abort_action_completer(processor_, this);
64
adlr@google.com3defe6a2009-12-04 20:57:17 +000065 if (!HasInputObject()) {
Andrew de los Reyesc7020782010-04-28 10:46:04 -070066 LOG(ERROR) << "FilesystemCopierAction missing input object.";
adlr@google.com3defe6a2009-12-04 20:57:17 +000067 return;
68 }
69 install_plan_ = GetInputObject();
70
Darin Petkov9b230572010-10-08 10:20:09 -070071 if (install_plan_.is_full_update || install_plan_.is_resume) {
Andrew de los Reyesc7020782010-04-28 10:46:04 -070072 // No copy needed. Done!
Andrew de los Reyesf98bff82010-05-06 13:33:25 -070073 if (HasOutputPipe())
74 SetOutputObject(install_plan_);
Darin Petkovc1a8b422010-07-19 11:34:49 -070075 abort_action_completer.set_code(kActionCodeSuccess);
adlr@google.com3defe6a2009-12-04 20:57:17 +000076 return;
77 }
78
Andrew de los Reyesf9185172010-05-03 11:07:05 -070079 string source = copy_source_;
80 if (source.empty()) {
81 source = copying_kernel_install_path_ ?
82 utils::BootKernelDevice(utils::BootDevice()) :
83 utils::BootDevice();
84 }
85
86 const string destination = copying_kernel_install_path_ ?
87 install_plan_.kernel_install_path :
88 install_plan_.install_path;
Darin Petkovc1a8b422010-07-19 11:34:49 -070089
Andrew de los Reyesc7020782010-04-28 10:46:04 -070090 int src_fd = open(source.c_str(), O_RDONLY);
91 if (src_fd < 0) {
92 PLOG(ERROR) << "Unable to open " << source << " for reading:";
93 return;
94 }
Andrew de los Reyesf9185172010-05-03 11:07:05 -070095 int dst_fd = open(destination.c_str(),
Andrew de los Reyesc7020782010-04-28 10:46:04 -070096 O_WRONLY | O_TRUNC | O_CREAT,
97 0644);
98 if (dst_fd < 0) {
99 close(src_fd);
100 PLOG(ERROR) << "Unable to open " << install_plan_.install_path
101 << " for writing:";
102 return;
adlr@google.com3defe6a2009-12-04 20:57:17 +0000103 }
104
Darin Petkov698d0412010-10-13 10:59:44 -0700105 DetermineFilesystemSize(src_fd);
106
Andrew de los Reyesc7020782010-04-28 10:46:04 -0700107 src_stream_ = g_unix_input_stream_new(src_fd, TRUE);
108 dst_stream_ = g_unix_output_stream_new(dst_fd, TRUE);
109
Darin Petkovc2e4a7d2010-12-02 14:47:06 -0800110 for (int i = 0; i < 2; i++) {
111 buffer_[i].resize(kCopyFileBufferSize);
112 canceller_[i] = g_cancellable_new();
113 }
Andrew de los Reyesc7020782010-04-28 10:46:04 -0700114
Darin Petkovc2e4a7d2010-12-02 14:47:06 -0800115 // Start the first read.
116 SpawnAsyncActions();
Andrew de los Reyesc7020782010-04-28 10:46:04 -0700117
118 abort_action_completer.set_should_complete(false);
adlr@google.com3defe6a2009-12-04 20:57:17 +0000119}
120
121void FilesystemCopierAction::TerminateProcessing() {
Darin Petkovc2e4a7d2010-12-02 14:47:06 -0800122 for (int i = 0; i < 2; i++) {
123 if (canceller_[i]) {
124 g_cancellable_cancel(canceller_[i]);
125 }
adlr@google.com3defe6a2009-12-04 20:57:17 +0000126 }
127}
128
Darin Petkovc2e4a7d2010-12-02 14:47:06 -0800129void FilesystemCopierAction::Cleanup(bool success) {
130 for (int i = 0; i < 2; i++) {
131 g_object_unref(canceller_[i]);
132 canceller_[i] = NULL;
133 }
Andrew de los Reyesc7020782010-04-28 10:46:04 -0700134 g_object_unref(src_stream_);
135 src_stream_ = NULL;
136 g_object_unref(dst_stream_);
137 dst_stream_ = NULL;
Darin Petkovc2e4a7d2010-12-02 14:47:06 -0800138 if (cancelled_)
Andrew de los Reyesc7020782010-04-28 10:46:04 -0700139 return;
140 if (success && HasOutputPipe())
adlr@google.com3defe6a2009-12-04 20:57:17 +0000141 SetOutputObject(install_plan_);
Darin Petkovc1a8b422010-07-19 11:34:49 -0700142 processor_->ActionComplete(
143 this,
144 success ? kActionCodeSuccess : kActionCodeError);
adlr@google.com3defe6a2009-12-04 20:57:17 +0000145}
146
Darin Petkovc2e4a7d2010-12-02 14:47:06 -0800147void FilesystemCopierAction::AsyncReadReadyCallback(GObject *source_object,
148 GAsyncResult *res) {
149 int index = buffer_state_[0] == kBufferStateReading ? 0 : 1;
150 CHECK(buffer_state_[index] == kBufferStateReading);
151
Andrew de los Reyesc7020782010-04-28 10:46:04 -0700152 GError* error = NULL;
Darin Petkovc2e4a7d2010-12-02 14:47:06 -0800153 CHECK(canceller_[index]);
154 cancelled_ = g_cancellable_is_cancelled(canceller_[index]) == TRUE;
adlr@google.com3defe6a2009-12-04 20:57:17 +0000155
Darin Petkovc2e4a7d2010-12-02 14:47:06 -0800156 ssize_t bytes_read = g_input_stream_read_finish(src_stream_, res, &error);
157 if (bytes_read < 0) {
158 LOG(ERROR) << "Read failed: " << utils::GetGErrorMessage(error);
159 failed_ = true;
160 buffer_state_[index] = kBufferStateEmpty;
161 } else if (bytes_read == 0) {
162 read_done_ = true;
163 buffer_state_[index] = kBufferStateEmpty;
164 } else {
165 buffer_valid_size_[index] = bytes_read;
166 buffer_state_[index] = kBufferStateFull;
167 }
168
169 if (bytes_read > 0) {
170 filesystem_size_ -= bytes_read;
171 }
172
173 SpawnAsyncActions();
174
175 if (bytes_read > 0) {
176 if (!hasher_.Update(buffer_[index].data(), bytes_read)) {
177 LOG(ERROR) << "Unable to update the hash.";
178 failed_ = true;
adlr@google.com3defe6a2009-12-04 20:57:17 +0000179 }
Darin Petkovc2e4a7d2010-12-02 14:47:06 -0800180 }
181}
Andrew de los Reyesc7020782010-04-28 10:46:04 -0700182
Darin Petkovc2e4a7d2010-12-02 14:47:06 -0800183void FilesystemCopierAction::StaticAsyncReadReadyCallback(
184 GObject *source_object,
185 GAsyncResult *res,
186 gpointer user_data) {
187 reinterpret_cast<FilesystemCopierAction*>(user_data)->
188 AsyncReadReadyCallback(source_object, res);
189}
190
191void FilesystemCopierAction::AsyncWriteReadyCallback(GObject *source_object,
192 GAsyncResult *res) {
193 int index = buffer_state_[0] == kBufferStateWriting ? 0 : 1;
194 CHECK(buffer_state_[index] == kBufferStateWriting);
195 buffer_state_[index] = kBufferStateEmpty;
196
197 GError* error = NULL;
198 CHECK(canceller_[index]);
199 cancelled_ = g_cancellable_is_cancelled(canceller_[index]) == TRUE;
200
201 ssize_t bytes_written = g_output_stream_write_finish(dst_stream_,
202 res,
203 &error);
204 if (bytes_written < static_cast<ssize_t>(buffer_valid_size_[index])) {
205 if (bytes_written < 0) {
206 LOG(ERROR) << "Write failed: " << utils::GetGErrorMessage(error);
207 } else {
208 LOG(ERROR) << "Write was short: wrote " << bytes_written
209 << " but expected to write " << buffer_valid_size_[index];
210 }
211 failed_ = true;
212 }
213
214 SpawnAsyncActions();
215}
216
217void FilesystemCopierAction::StaticAsyncWriteReadyCallback(
218 GObject *source_object,
219 GAsyncResult *res,
220 gpointer user_data) {
221 reinterpret_cast<FilesystemCopierAction*>(user_data)->
222 AsyncWriteReadyCallback(source_object, res);
223}
224
225void FilesystemCopierAction::SpawnAsyncActions() {
226 bool reading = false;
227 bool writing = false;
228 for (int i = 0; i < 2; i++) {
229 if (buffer_state_[i] == kBufferStateReading) {
230 reading = true;
231 }
232 if (buffer_state_[i] == kBufferStateWriting) {
233 writing = true;
234 }
235 }
236 if (failed_ || cancelled_) {
237 if (!reading && !writing) {
238 Cleanup(false);
239 }
240 return;
241 }
242 for (int i = 0; i < 2; i++) {
243 if (!reading && !read_done_ && buffer_state_[i] == kBufferStateEmpty) {
244 g_input_stream_read_async(
245 src_stream_,
246 buffer_[i].data(),
247 GetBytesToRead(),
248 G_PRIORITY_DEFAULT,
249 canceller_[i],
250 &FilesystemCopierAction::StaticAsyncReadReadyCallback,
251 this);
252 reading = true;
253 buffer_state_[i] = kBufferStateReading;
254 } else if (!writing && buffer_state_[i] == kBufferStateFull) {
255 g_output_stream_write_async(
256 dst_stream_,
257 buffer_[i].data(),
258 buffer_valid_size_[i],
259 G_PRIORITY_DEFAULT,
260 canceller_[i],
261 &FilesystemCopierAction::StaticAsyncWriteReadyCallback,
262 this);
263 writing = true;
264 buffer_state_[i] = kBufferStateWriting;
265 }
266 }
267 if (!reading && !writing) {
268 // We're done!
269 if (hasher_.Finalize()) {
Darin Petkov698d0412010-10-13 10:59:44 -0700270 LOG(INFO) << "hash: " << hasher_.hash();
271 if (copying_kernel_install_path_) {
272 install_plan_.current_kernel_hash = hasher_.raw_hash();
273 } else {
274 install_plan_.current_rootfs_hash = hasher_.raw_hash();
275 }
Darin Petkovc2e4a7d2010-12-02 14:47:06 -0800276 Cleanup(true);
adlr@google.com3defe6a2009-12-04 20:57:17 +0000277 } else {
Darin Petkovc2e4a7d2010-12-02 14:47:06 -0800278 LOG(ERROR) << "Unable to finalize the hash.";
279 Cleanup(false);
adlr@google.com3defe6a2009-12-04 20:57:17 +0000280 }
adlr@google.com3defe6a2009-12-04 20:57:17 +0000281 }
Andrew de los Reyesc7020782010-04-28 10:46:04 -0700282}
adlr@google.com3defe6a2009-12-04 20:57:17 +0000283
Darin Petkov698d0412010-10-13 10:59:44 -0700284void FilesystemCopierAction::DetermineFilesystemSize(int fd) {
285 filesystem_size_ = kint64max;
286 int block_count = 0, block_size = 0;
287 if (!copying_kernel_install_path_ &&
288 utils::GetFilesystemSizeFromFD(fd, &block_count, &block_size)) {
289 filesystem_size_ = static_cast<int64_t>(block_count) * block_size;
290 LOG(INFO) << "Filesystem size: " << filesystem_size_ << " bytes ("
291 << block_count << "x" << block_size << ").";
292 }
293}
294
295int64_t FilesystemCopierAction::GetBytesToRead() {
Darin Petkovc2e4a7d2010-12-02 14:47:06 -0800296 return std::min(static_cast<int64_t>(buffer_[0].size()), filesystem_size_);
Darin Petkov698d0412010-10-13 10:59:44 -0700297}
298
adlr@google.com3defe6a2009-12-04 20:57:17 +0000299} // namespace chromeos_update_engine