blob: 388e0e7a88b57fce7cfbc7582b2fde39d125bba6 [file] [log] [blame]
Andrew de los Reyesc7020782010-04-28 10:46:04 -07001// Copyright (c) 2010 The Chromium 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"
6#include <sys/stat.h>
7#include <sys/types.h>
8#include <errno.h>
9#include <fcntl.h>
10#include <stdlib.h>
11#include <algorithm>
Andrew de los Reyes4fe15d02009-12-10 19:01:36 -080012#include <map>
adlr@google.com3defe6a2009-12-04 20:57:17 +000013#include <string>
14#include <vector>
Andrew de los Reyesc7020782010-04-28 10:46:04 -070015#include <gio/gio.h>
16#include <gio/gunixinputstream.h>
17#include <gio/gunixoutputstream.h>
18#include <glib.h>
adlr@google.com3defe6a2009-12-04 20:57:17 +000019#include "update_engine/filesystem_iterator.h"
20#include "update_engine/subprocess.h"
21#include "update_engine/utils.h"
22
Andrew de los Reyes4fe15d02009-12-10 19:01:36 -080023using std::map;
adlr@google.com3defe6a2009-12-04 20:57:17 +000024using std::min;
25using std::string;
26using std::vector;
27
28namespace chromeos_update_engine {
29
30namespace {
Andrew de los Reyesc7020782010-04-28 10:46:04 -070031const off_t kCopyFileBufferSize = 2 * 1024 * 1024;
adlr@google.com3defe6a2009-12-04 20:57:17 +000032} // namespace {}
33
34void FilesystemCopierAction::PerformAction() {
Andrew de los Reyesc7020782010-04-28 10:46:04 -070035 // Will tell the ActionProcessor we've failed if we return.
36 ScopedActionCompleter abort_action_completer(processor_, this);
37
adlr@google.com3defe6a2009-12-04 20:57:17 +000038 if (!HasInputObject()) {
Andrew de los Reyesc7020782010-04-28 10:46:04 -070039 LOG(ERROR) << "FilesystemCopierAction missing input object.";
adlr@google.com3defe6a2009-12-04 20:57:17 +000040 return;
41 }
42 install_plan_ = GetInputObject();
43
44 if (install_plan_.is_full_update) {
Andrew de los Reyesc7020782010-04-28 10:46:04 -070045 // No copy needed. Done!
Andrew de los Reyesf98bff82010-05-06 13:33:25 -070046 if (HasOutputPipe())
47 SetOutputObject(install_plan_);
Darin Petkovc1a8b422010-07-19 11:34:49 -070048 abort_action_completer.set_code(kActionCodeSuccess);
adlr@google.com3defe6a2009-12-04 20:57:17 +000049 return;
50 }
51
Andrew de los Reyesf9185172010-05-03 11:07:05 -070052 string source = copy_source_;
53 if (source.empty()) {
54 source = copying_kernel_install_path_ ?
55 utils::BootKernelDevice(utils::BootDevice()) :
56 utils::BootDevice();
57 }
58
59 const string destination = copying_kernel_install_path_ ?
60 install_plan_.kernel_install_path :
61 install_plan_.install_path;
Darin Petkovc1a8b422010-07-19 11:34:49 -070062
Andrew de los Reyesc7020782010-04-28 10:46:04 -070063 int src_fd = open(source.c_str(), O_RDONLY);
64 if (src_fd < 0) {
65 PLOG(ERROR) << "Unable to open " << source << " for reading:";
66 return;
67 }
Andrew de los Reyesf9185172010-05-03 11:07:05 -070068 int dst_fd = open(destination.c_str(),
Andrew de los Reyesc7020782010-04-28 10:46:04 -070069 O_WRONLY | O_TRUNC | O_CREAT,
70 0644);
71 if (dst_fd < 0) {
72 close(src_fd);
73 PLOG(ERROR) << "Unable to open " << install_plan_.install_path
74 << " for writing:";
75 return;
adlr@google.com3defe6a2009-12-04 20:57:17 +000076 }
77
Andrew de los Reyesc7020782010-04-28 10:46:04 -070078 src_stream_ = g_unix_input_stream_new(src_fd, TRUE);
79 dst_stream_ = g_unix_output_stream_new(dst_fd, TRUE);
80
81 buffer_.resize(kCopyFileBufferSize);
82
83 // Set up the first read
84 canceller_ = g_cancellable_new();
85
86 g_input_stream_read_async(src_stream_,
87 &buffer_[0],
88 buffer_.size(),
89 G_PRIORITY_DEFAULT,
90 canceller_,
91 &FilesystemCopierAction::StaticAsyncReadyCallback,
92 this);
93 read_in_flight_ = true;
94
95 abort_action_completer.set_should_complete(false);
adlr@google.com3defe6a2009-12-04 20:57:17 +000096}
97
98void FilesystemCopierAction::TerminateProcessing() {
Andrew de los Reyesc7020782010-04-28 10:46:04 -070099 if (canceller_) {
100 g_cancellable_cancel(canceller_);
adlr@google.com3defe6a2009-12-04 20:57:17 +0000101 }
102}
103
Andrew de los Reyesc7020782010-04-28 10:46:04 -0700104void FilesystemCopierAction::Cleanup(bool success, bool was_cancelled) {
105 g_object_unref(src_stream_);
106 src_stream_ = NULL;
107 g_object_unref(dst_stream_);
108 dst_stream_ = NULL;
109 if (was_cancelled)
110 return;
111 if (success && HasOutputPipe())
adlr@google.com3defe6a2009-12-04 20:57:17 +0000112 SetOutputObject(install_plan_);
Darin Petkovc1a8b422010-07-19 11:34:49 -0700113 processor_->ActionComplete(
114 this,
115 success ? kActionCodeSuccess : kActionCodeError);
adlr@google.com3defe6a2009-12-04 20:57:17 +0000116}
117
Andrew de los Reyesc7020782010-04-28 10:46:04 -0700118void FilesystemCopierAction::AsyncReadyCallback(GObject *source_object,
119 GAsyncResult *res) {
120 GError* error = NULL;
121 CHECK(canceller_);
122 bool was_cancelled = g_cancellable_is_cancelled(canceller_) == TRUE;
123 g_object_unref(canceller_);
124 canceller_ = NULL;
adlr@google.com3defe6a2009-12-04 20:57:17 +0000125
Andrew de los Reyesc7020782010-04-28 10:46:04 -0700126 if (read_in_flight_) {
127 ssize_t bytes_read = g_input_stream_read_finish(src_stream_, res, &error);
128 if (bytes_read < 0) {
129 LOG(ERROR) << "Read failed:" << utils::GetGErrorMessage(error);
130 Cleanup(false, was_cancelled);
131 return;
adlr@google.com3defe6a2009-12-04 20:57:17 +0000132 }
Andrew de los Reyesc7020782010-04-28 10:46:04 -0700133
134 if (bytes_read == 0) {
135 // We're done!
136 Cleanup(true, was_cancelled);
137 return;
138 }
139 // Kick off a write
140 read_in_flight_ = false;
141 buffer_valid_size_ = bytes_read;
142 canceller_ = g_cancellable_new();
143 g_output_stream_write_async(
144 dst_stream_,
145 &buffer_[0],
146 bytes_read,
147 G_PRIORITY_DEFAULT,
148 canceller_,
149 &FilesystemCopierAction::StaticAsyncReadyCallback,
150 this);
151 return;
adlr@google.com3defe6a2009-12-04 20:57:17 +0000152 }
adlr@google.com3defe6a2009-12-04 20:57:17 +0000153
Andrew de los Reyesc7020782010-04-28 10:46:04 -0700154 ssize_t bytes_written = g_output_stream_write_finish(dst_stream_,
155 res,
156 &error);
157 if (bytes_written < static_cast<ssize_t>(buffer_valid_size_)) {
158 if (bytes_written < 0) {
159 LOG(ERROR) << "Write failed:" << utils::GetGErrorMessage(error);
adlr@google.com3defe6a2009-12-04 20:57:17 +0000160 } else {
Andrew de los Reyesc7020782010-04-28 10:46:04 -0700161 LOG(ERROR) << "Write was short: wrote " << bytes_written
162 << " but expected to write " << buffer_valid_size_;
adlr@google.com3defe6a2009-12-04 20:57:17 +0000163 }
Andrew de los Reyesc7020782010-04-28 10:46:04 -0700164 Cleanup(false, was_cancelled);
165 return;
adlr@google.com3defe6a2009-12-04 20:57:17 +0000166 }
adlr@google.com3defe6a2009-12-04 20:57:17 +0000167
Andrew de los Reyesc7020782010-04-28 10:46:04 -0700168 // Kick off a read
169 read_in_flight_ = true;
170 canceller_ = g_cancellable_new();
171 g_input_stream_read_async(
172 src_stream_,
173 &buffer_[0],
174 buffer_.size(),
175 G_PRIORITY_DEFAULT,
176 canceller_,
177 &FilesystemCopierAction::StaticAsyncReadyCallback,
178 this);
179}
adlr@google.com3defe6a2009-12-04 20:57:17 +0000180
181} // namespace chromeos_update_engine