blob: 388e0e7a88b57fce7cfbc7582b2fde39d125bba6 [file] [log] [blame]
// Copyright (c) 2010 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "update_engine/filesystem_copier_action.h"
#include <sys/stat.h>
#include <sys/types.h>
#include <errno.h>
#include <fcntl.h>
#include <stdlib.h>
#include <algorithm>
#include <map>
#include <string>
#include <vector>
#include <gio/gio.h>
#include <gio/gunixinputstream.h>
#include <gio/gunixoutputstream.h>
#include <glib.h>
#include "update_engine/filesystem_iterator.h"
#include "update_engine/subprocess.h"
#include "update_engine/utils.h"
using std::map;
using std::min;
using std::string;
using std::vector;
namespace chromeos_update_engine {
namespace {
const off_t kCopyFileBufferSize = 2 * 1024 * 1024;
} // namespace {}
void FilesystemCopierAction::PerformAction() {
// Will tell the ActionProcessor we've failed if we return.
ScopedActionCompleter abort_action_completer(processor_, this);
if (!HasInputObject()) {
LOG(ERROR) << "FilesystemCopierAction missing input object.";
return;
}
install_plan_ = GetInputObject();
if (install_plan_.is_full_update) {
// No copy needed. Done!
if (HasOutputPipe())
SetOutputObject(install_plan_);
abort_action_completer.set_code(kActionCodeSuccess);
return;
}
string source = copy_source_;
if (source.empty()) {
source = copying_kernel_install_path_ ?
utils::BootKernelDevice(utils::BootDevice()) :
utils::BootDevice();
}
const string destination = copying_kernel_install_path_ ?
install_plan_.kernel_install_path :
install_plan_.install_path;
int src_fd = open(source.c_str(), O_RDONLY);
if (src_fd < 0) {
PLOG(ERROR) << "Unable to open " << source << " for reading:";
return;
}
int dst_fd = open(destination.c_str(),
O_WRONLY | O_TRUNC | O_CREAT,
0644);
if (dst_fd < 0) {
close(src_fd);
PLOG(ERROR) << "Unable to open " << install_plan_.install_path
<< " for writing:";
return;
}
src_stream_ = g_unix_input_stream_new(src_fd, TRUE);
dst_stream_ = g_unix_output_stream_new(dst_fd, TRUE);
buffer_.resize(kCopyFileBufferSize);
// Set up the first read
canceller_ = g_cancellable_new();
g_input_stream_read_async(src_stream_,
&buffer_[0],
buffer_.size(),
G_PRIORITY_DEFAULT,
canceller_,
&FilesystemCopierAction::StaticAsyncReadyCallback,
this);
read_in_flight_ = true;
abort_action_completer.set_should_complete(false);
}
void FilesystemCopierAction::TerminateProcessing() {
if (canceller_) {
g_cancellable_cancel(canceller_);
}
}
void FilesystemCopierAction::Cleanup(bool success, bool was_cancelled) {
g_object_unref(src_stream_);
src_stream_ = NULL;
g_object_unref(dst_stream_);
dst_stream_ = NULL;
if (was_cancelled)
return;
if (success && HasOutputPipe())
SetOutputObject(install_plan_);
processor_->ActionComplete(
this,
success ? kActionCodeSuccess : kActionCodeError);
}
void FilesystemCopierAction::AsyncReadyCallback(GObject *source_object,
GAsyncResult *res) {
GError* error = NULL;
CHECK(canceller_);
bool was_cancelled = g_cancellable_is_cancelled(canceller_) == TRUE;
g_object_unref(canceller_);
canceller_ = NULL;
if (read_in_flight_) {
ssize_t bytes_read = g_input_stream_read_finish(src_stream_, res, &error);
if (bytes_read < 0) {
LOG(ERROR) << "Read failed:" << utils::GetGErrorMessage(error);
Cleanup(false, was_cancelled);
return;
}
if (bytes_read == 0) {
// We're done!
Cleanup(true, was_cancelled);
return;
}
// Kick off a write
read_in_flight_ = false;
buffer_valid_size_ = bytes_read;
canceller_ = g_cancellable_new();
g_output_stream_write_async(
dst_stream_,
&buffer_[0],
bytes_read,
G_PRIORITY_DEFAULT,
canceller_,
&FilesystemCopierAction::StaticAsyncReadyCallback,
this);
return;
}
ssize_t bytes_written = g_output_stream_write_finish(dst_stream_,
res,
&error);
if (bytes_written < static_cast<ssize_t>(buffer_valid_size_)) {
if (bytes_written < 0) {
LOG(ERROR) << "Write failed:" << utils::GetGErrorMessage(error);
} else {
LOG(ERROR) << "Write was short: wrote " << bytes_written
<< " but expected to write " << buffer_valid_size_;
}
Cleanup(false, was_cancelled);
return;
}
// Kick off a read
read_in_flight_ = true;
canceller_ = g_cancellable_new();
g_input_stream_read_async(
src_stream_,
&buffer_[0],
buffer_.size(),
G_PRIORITY_DEFAULT,
canceller_,
&FilesystemCopierAction::StaticAsyncReadyCallback,
this);
}
} // namespace chromeos_update_engine