blob: 1452b4dbffed38399e04f39d462a6b5313d139b0 [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 {
Andrew de los Reyesc7020782010-04-28 10:46:04 -070035const off_t kCopyFileBufferSize = 2 * 1024 * 1024;
adlr@google.com3defe6a2009-12-04 20:57:17 +000036} // namespace {}
37
38void FilesystemCopierAction::PerformAction() {
Andrew de los Reyesc7020782010-04-28 10:46:04 -070039 // Will tell the ActionProcessor we've failed if we return.
40 ScopedActionCompleter abort_action_completer(processor_, this);
41
adlr@google.com3defe6a2009-12-04 20:57:17 +000042 if (!HasInputObject()) {
Andrew de los Reyesc7020782010-04-28 10:46:04 -070043 LOG(ERROR) << "FilesystemCopierAction missing input object.";
adlr@google.com3defe6a2009-12-04 20:57:17 +000044 return;
45 }
46 install_plan_ = GetInputObject();
47
Darin Petkov9b230572010-10-08 10:20:09 -070048 if (install_plan_.is_full_update || install_plan_.is_resume) {
Andrew de los Reyesc7020782010-04-28 10:46:04 -070049 // No copy needed. Done!
Andrew de los Reyesf98bff82010-05-06 13:33:25 -070050 if (HasOutputPipe())
51 SetOutputObject(install_plan_);
Darin Petkovc1a8b422010-07-19 11:34:49 -070052 abort_action_completer.set_code(kActionCodeSuccess);
adlr@google.com3defe6a2009-12-04 20:57:17 +000053 return;
54 }
55
Andrew de los Reyesf9185172010-05-03 11:07:05 -070056 string source = copy_source_;
57 if (source.empty()) {
58 source = copying_kernel_install_path_ ?
59 utils::BootKernelDevice(utils::BootDevice()) :
60 utils::BootDevice();
61 }
62
63 const string destination = copying_kernel_install_path_ ?
64 install_plan_.kernel_install_path :
65 install_plan_.install_path;
Darin Petkovc1a8b422010-07-19 11:34:49 -070066
Andrew de los Reyesc7020782010-04-28 10:46:04 -070067 int src_fd = open(source.c_str(), O_RDONLY);
68 if (src_fd < 0) {
69 PLOG(ERROR) << "Unable to open " << source << " for reading:";
70 return;
71 }
Andrew de los Reyesf9185172010-05-03 11:07:05 -070072 int dst_fd = open(destination.c_str(),
Andrew de los Reyesc7020782010-04-28 10:46:04 -070073 O_WRONLY | O_TRUNC | O_CREAT,
74 0644);
75 if (dst_fd < 0) {
76 close(src_fd);
77 PLOG(ERROR) << "Unable to open " << install_plan_.install_path
78 << " for writing:";
79 return;
adlr@google.com3defe6a2009-12-04 20:57:17 +000080 }
81
Darin Petkov698d0412010-10-13 10:59:44 -070082 DetermineFilesystemSize(src_fd);
83
Andrew de los Reyesc7020782010-04-28 10:46:04 -070084 src_stream_ = g_unix_input_stream_new(src_fd, TRUE);
85 dst_stream_ = g_unix_output_stream_new(dst_fd, TRUE);
86
87 buffer_.resize(kCopyFileBufferSize);
88
89 // Set up the first read
90 canceller_ = g_cancellable_new();
91
92 g_input_stream_read_async(src_stream_,
93 &buffer_[0],
Darin Petkov698d0412010-10-13 10:59:44 -070094 GetBytesToRead(),
Andrew de los Reyesc7020782010-04-28 10:46:04 -070095 G_PRIORITY_DEFAULT,
96 canceller_,
97 &FilesystemCopierAction::StaticAsyncReadyCallback,
98 this);
99 read_in_flight_ = true;
100
101 abort_action_completer.set_should_complete(false);
adlr@google.com3defe6a2009-12-04 20:57:17 +0000102}
103
104void FilesystemCopierAction::TerminateProcessing() {
Andrew de los Reyesc7020782010-04-28 10:46:04 -0700105 if (canceller_) {
106 g_cancellable_cancel(canceller_);
adlr@google.com3defe6a2009-12-04 20:57:17 +0000107 }
108}
109
Andrew de los Reyesc7020782010-04-28 10:46:04 -0700110void FilesystemCopierAction::Cleanup(bool success, bool was_cancelled) {
111 g_object_unref(src_stream_);
112 src_stream_ = NULL;
113 g_object_unref(dst_stream_);
114 dst_stream_ = NULL;
115 if (was_cancelled)
116 return;
117 if (success && HasOutputPipe())
adlr@google.com3defe6a2009-12-04 20:57:17 +0000118 SetOutputObject(install_plan_);
Darin Petkovc1a8b422010-07-19 11:34:49 -0700119 processor_->ActionComplete(
120 this,
121 success ? kActionCodeSuccess : kActionCodeError);
adlr@google.com3defe6a2009-12-04 20:57:17 +0000122}
123
Andrew de los Reyesc7020782010-04-28 10:46:04 -0700124void FilesystemCopierAction::AsyncReadyCallback(GObject *source_object,
125 GAsyncResult *res) {
126 GError* error = NULL;
127 CHECK(canceller_);
128 bool was_cancelled = g_cancellable_is_cancelled(canceller_) == TRUE;
129 g_object_unref(canceller_);
130 canceller_ = NULL;
adlr@google.com3defe6a2009-12-04 20:57:17 +0000131
Andrew de los Reyesc7020782010-04-28 10:46:04 -0700132 if (read_in_flight_) {
133 ssize_t bytes_read = g_input_stream_read_finish(src_stream_, res, &error);
134 if (bytes_read < 0) {
135 LOG(ERROR) << "Read failed:" << utils::GetGErrorMessage(error);
136 Cleanup(false, was_cancelled);
137 return;
adlr@google.com3defe6a2009-12-04 20:57:17 +0000138 }
Andrew de los Reyesc7020782010-04-28 10:46:04 -0700139
140 if (bytes_read == 0) {
141 // We're done!
Darin Petkov698d0412010-10-13 10:59:44 -0700142 if (!hasher_.Finalize()) {
143 LOG(ERROR) << "Unable to finalize the hash.";
144 Cleanup(false, was_cancelled);
145 return;
146 }
147 LOG(INFO) << "hash: " << hasher_.hash();
148 if (copying_kernel_install_path_) {
149 install_plan_.current_kernel_hash = hasher_.raw_hash();
150 } else {
151 install_plan_.current_rootfs_hash = hasher_.raw_hash();
152 }
Andrew de los Reyesc7020782010-04-28 10:46:04 -0700153 Cleanup(true, was_cancelled);
154 return;
155 }
Darin Petkov698d0412010-10-13 10:59:44 -0700156 if (!hasher_.Update(buffer_.data(), bytes_read)) {
157 LOG(ERROR) << "Unable to update the hash.";
158 Cleanup(false, was_cancelled);
159 return;
160 }
161 filesystem_size_ -= bytes_read;
162
Andrew de los Reyesc7020782010-04-28 10:46:04 -0700163 // Kick off a write
164 read_in_flight_ = false;
165 buffer_valid_size_ = bytes_read;
166 canceller_ = g_cancellable_new();
167 g_output_stream_write_async(
168 dst_stream_,
169 &buffer_[0],
170 bytes_read,
171 G_PRIORITY_DEFAULT,
172 canceller_,
173 &FilesystemCopierAction::StaticAsyncReadyCallback,
174 this);
175 return;
adlr@google.com3defe6a2009-12-04 20:57:17 +0000176 }
adlr@google.com3defe6a2009-12-04 20:57:17 +0000177
Andrew de los Reyesc7020782010-04-28 10:46:04 -0700178 ssize_t bytes_written = g_output_stream_write_finish(dst_stream_,
179 res,
180 &error);
181 if (bytes_written < static_cast<ssize_t>(buffer_valid_size_)) {
182 if (bytes_written < 0) {
183 LOG(ERROR) << "Write failed:" << utils::GetGErrorMessage(error);
adlr@google.com3defe6a2009-12-04 20:57:17 +0000184 } else {
Andrew de los Reyesc7020782010-04-28 10:46:04 -0700185 LOG(ERROR) << "Write was short: wrote " << bytes_written
186 << " but expected to write " << buffer_valid_size_;
adlr@google.com3defe6a2009-12-04 20:57:17 +0000187 }
Andrew de los Reyesc7020782010-04-28 10:46:04 -0700188 Cleanup(false, was_cancelled);
189 return;
adlr@google.com3defe6a2009-12-04 20:57:17 +0000190 }
adlr@google.com3defe6a2009-12-04 20:57:17 +0000191
Andrew de los Reyesc7020782010-04-28 10:46:04 -0700192 // Kick off a read
193 read_in_flight_ = true;
194 canceller_ = g_cancellable_new();
195 g_input_stream_read_async(
196 src_stream_,
197 &buffer_[0],
Darin Petkov698d0412010-10-13 10:59:44 -0700198 GetBytesToRead(),
Andrew de los Reyesc7020782010-04-28 10:46:04 -0700199 G_PRIORITY_DEFAULT,
200 canceller_,
201 &FilesystemCopierAction::StaticAsyncReadyCallback,
202 this);
203}
adlr@google.com3defe6a2009-12-04 20:57:17 +0000204
Darin Petkov698d0412010-10-13 10:59:44 -0700205void FilesystemCopierAction::DetermineFilesystemSize(int fd) {
206 filesystem_size_ = kint64max;
207 int block_count = 0, block_size = 0;
208 if (!copying_kernel_install_path_ &&
209 utils::GetFilesystemSizeFromFD(fd, &block_count, &block_size)) {
210 filesystem_size_ = static_cast<int64_t>(block_count) * block_size;
211 LOG(INFO) << "Filesystem size: " << filesystem_size_ << " bytes ("
212 << block_count << "x" << block_size << ").";
213 }
214}
215
216int64_t FilesystemCopierAction::GetBytesToRead() {
217 return std::min(static_cast<int64_t>(buffer_.size()), filesystem_size_);
218}
219
adlr@google.com3defe6a2009-12-04 20:57:17 +0000220} // namespace chromeos_update_engine