Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(435)

Unified Diff: src/platform/update_engine/filesystem_copier_action.cc

Issue 1700018: AU: FilesystemCopierAction: copy bit-exactly (Closed) Base URL: ssh://git@chromiumos-git/chromeos
Patch Set: fixes for review Created 10 years, 8 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: src/platform/update_engine/filesystem_copier_action.cc
diff --git a/src/platform/update_engine/filesystem_copier_action.cc b/src/platform/update_engine/filesystem_copier_action.cc
old mode 100644
new mode 100755
index 2a361eed74c8e6b1bf6cf7672a07a70a1b40387d..18bab4464180f030fdcc12500f27cc9407ab4b1b
--- a/src/platform/update_engine/filesystem_copier_action.cc
+++ b/src/platform/update_engine/filesystem_copier_action.cc
@@ -1,4 +1,4 @@
-// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// 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.
@@ -12,6 +12,10 @@
#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"
@@ -24,284 +28,144 @@ using std::vector;
namespace chromeos_update_engine {
namespace {
-const char* kMountpointTemplate = "/tmp/au_dest_mnt.XXXXXX";
-const off_t kCopyFileBufferSize = 4 * 1024 * 1024;
-const char* kCopyExclusionPrefix = "/lost+found";
+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) << "No input object. Aborting.";
- processor_->ActionComplete(this, false);
+ LOG(ERROR) << "FilesystemCopierAction missing input object.";
return;
}
install_plan_ = GetInputObject();
if (install_plan_.is_full_update) {
- // No copy needed.
- processor_->ActionComplete(this, true);
+ // No copy needed. Done!
+ abort_action_completer.set_success(true);
return;
}
- {
- // Set up dest_path_
- char *dest_path_temp = strdup(kMountpointTemplate);
- CHECK(dest_path_temp);
- CHECK_EQ(mkdtemp(dest_path_temp), dest_path_temp);
- CHECK_NE(dest_path_temp[0], '\0');
- dest_path_ = dest_path_temp;
- free(dest_path_temp);
+ const string source =
+ copy_source_.empty() ? utils::BootDevice() : copy_source_;
+ LOG(INFO) << "Copying from " << source << " to "
+ << 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;
}
-
- // Make sure we're properly mounted
- if (Mount(install_plan_.install_path, dest_path_)) {
- bool done_early = false;
- if (utils::FileExists(
- (dest_path_ +
- FilesystemCopierAction::kCompleteFilesystemMarker).c_str())) {
- // We're done!
- done_early = true;
- skipped_copy_ = true;
- if (HasOutputPipe())
- SetOutputObject(install_plan_);
- }
- if (!Unmount(dest_path_)) {
- LOG(ERROR) << "Unmount failed. Aborting.";
- processor_->ActionComplete(this, false);
- return;
- }
- if (done_early) {
- CHECK(!is_mounted_);
- if (rmdir(dest_path_.c_str()) != 0)
- LOG(ERROR) << "Unable to remove " << dest_path_;
- processor_->ActionComplete(this, true);
- return;
- }
+ int dst_fd = open(install_plan_.install_path.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;
}
- LOG(ERROR) << "not mounted; spawning thread";
- // If we get here, mount failed or we're not done yet. Reformat and copy.
- CHECK_EQ(pthread_create(&helper_thread_, NULL, HelperThreadMainStatic, this),
- 0);
-}
-void FilesystemCopierAction::TerminateProcessing() {
- if (is_mounted_) {
- LOG(ERROR) << "Aborted processing, but left a filesystem mounted.";
- }
-}
+ src_stream_ = g_unix_input_stream_new(src_fd, TRUE);
+ dst_stream_ = g_unix_output_stream_new(dst_fd, TRUE);
-bool FilesystemCopierAction::Mount(const string& device,
- const string& mountpoint) {
- CHECK(!is_mounted_);
- if(utils::MountFilesystem(device, mountpoint, 0))
- is_mounted_ = true;
- return is_mounted_;
-}
+ buffer_.resize(kCopyFileBufferSize);
-bool FilesystemCopierAction::Unmount(const string& mountpoint) {
- CHECK(is_mounted_);
- if (utils::UnmountFilesystem(mountpoint))
- is_mounted_ = false;
- return !is_mounted_;
-}
+ // Set up the first read
+ canceller_ = g_cancellable_new();
-void* FilesystemCopierAction::HelperThreadMain() {
- // First, format the drive
- vector<string> cmd;
- cmd.push_back("/sbin/mkfs.ext3");
- cmd.push_back("-F");
- cmd.push_back(install_plan_.install_path);
- int return_code = 1;
- bool success = Subprocess::SynchronousExec(cmd, &return_code);
- if (return_code != 0) {
- LOG(INFO) << "Format of " << install_plan_.install_path
- << " failed. Exit code: " << return_code;
- success = false;
- }
- if (success) {
- if (!Mount(install_plan_.install_path, dest_path_)) {
- LOG(ERROR) << "Mount failed. Aborting";
- success = false;
- }
- }
- if (success) {
- success = CopySynchronously();
- }
- if (success) {
- // Place our marker to avoid copies again in the future
- int r = open((dest_path_ +
- FilesystemCopierAction::kCompleteFilesystemMarker).c_str(),
- O_CREAT | O_WRONLY, 0644);
- if (r >= 0)
- close(r);
- }
- // Unmount
- if (!Unmount(dest_path_)) {
- LOG(ERROR) << "Unmount failed. Aborting";
- success = false;
- }
- if (HasOutputPipe())
- SetOutputObject(install_plan_);
+ g_input_stream_read_async(src_stream_,
+ &buffer_[0],
+ buffer_.size(),
+ G_PRIORITY_DEFAULT,
+ canceller_,
+ &FilesystemCopierAction::StaticAsyncReadyCallback,
+ this);
+ read_in_flight_ = true;
- // Tell main thread that we're done
- g_timeout_add(0, CollectThreadStatic, this);
- return reinterpret_cast<void*>(success ? 0 : 1);
+ abort_action_completer.set_should_complete(false);
}
-void FilesystemCopierAction::CollectThread() {
- void *thread_ret_value = NULL;
- CHECK_EQ(pthread_join(helper_thread_, &thread_ret_value), 0);
- bool success = (thread_ret_value == 0);
- CHECK(!is_mounted_);
- if (rmdir(dest_path_.c_str()) != 0)
- LOG(INFO) << "Unable to remove " << dest_path_;
- LOG(INFO) << "FilesystemCopierAction done";
- processor_->ActionComplete(this, success);
+void FilesystemCopierAction::TerminateProcessing() {
+ if (canceller_) {
+ g_cancellable_cancel(canceller_);
+ }
}
-bool FilesystemCopierAction::CreateDirSynchronously(const std::string& new_path,
- const struct stat& stbuf) {
- int r = mkdir(new_path.c_str(), stbuf.st_mode);
- TEST_AND_RETURN_FALSE_ERRNO(r == 0);
- return true;
+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);
}
-bool FilesystemCopierAction::CopyFileSynchronously(const std::string& old_path,
- const std::string& new_path,
- const struct stat& stbuf) {
- int fd_out = open(new_path.c_str(), O_CREAT | O_EXCL | O_WRONLY,
- stbuf.st_mode);
- TEST_AND_RETURN_FALSE_ERRNO(fd_out >= 0);
- ScopedFdCloser fd_out_closer(&fd_out);
- int fd_in = open(old_path.c_str(), O_RDONLY, 0);
- TEST_AND_RETURN_FALSE_ERRNO(fd_in >= 0);
- ScopedFdCloser fd_in_closer(&fd_in);
-
- vector<char> buf(min(kCopyFileBufferSize, stbuf.st_size));
- off_t bytes_written = 0;
- while (true) {
- // Make sure we don't need to abort early:
- TEST_AND_RETURN_FALSE(!g_atomic_int_get(&thread_should_exit_));
-
- ssize_t read_size = read(fd_in, &buf[0], buf.size());
- TEST_AND_RETURN_FALSE_ERRNO(read_size >= 0);
- if (0 == read_size) // EOF
- break;
+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;
+ }
- ssize_t write_size = 0;
- while (write_size < read_size) {
- ssize_t r = write(fd_out, &buf[write_size], read_size - write_size);
- TEST_AND_RETURN_FALSE_ERRNO(r >= 0);
- write_size += r;
+ if (bytes_read == 0) {
+ // We're done!
+ Cleanup(true, was_cancelled);
+ return;
}
- CHECK_EQ(write_size, read_size);
- bytes_written += write_size;
- CHECK_LE(bytes_written, stbuf.st_size);
- if (bytes_written == stbuf.st_size)
- break;
+ // 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;
}
- CHECK_EQ(bytes_written, stbuf.st_size);
- return true;
-}
-
-bool FilesystemCopierAction::CreateHardLinkSynchronously(
- const std::string& old_path,
- const std::string& new_path) {
- int r = link(old_path.c_str(), new_path.c_str());
- TEST_AND_RETURN_FALSE_ERRNO(r == 0);
- return true;
-}
-bool FilesystemCopierAction::CopySymlinkSynchronously(
- const std::string& old_path,
- const std::string& new_path,
- const struct stat& stbuf) {
- vector<char> buf(PATH_MAX + 1);
- ssize_t r = readlink(old_path.c_str(), &buf[0], buf.size());
- TEST_AND_RETURN_FALSE_ERRNO(r >= 0);
- // Make sure we got the entire link
- TEST_AND_RETURN_FALSE(static_cast<unsigned>(r) < buf.size());
- buf[r] = '\0';
- int rc = symlink(&buf[0], new_path.c_str());
- TEST_AND_RETURN_FALSE_ERRNO(rc == 0);
- return true;
-}
-
-bool FilesystemCopierAction::CreateNodeSynchronously(
- const std::string& new_path,
- const struct stat& stbuf) {
- int r = mknod(new_path.c_str(), stbuf.st_mode, stbuf.st_rdev);
- TEST_AND_RETURN_FALSE_ERRNO(r == 0);
- return true;
-}
-
-// Returns true on success
-bool FilesystemCopierAction::CopySynchronously() {
- // This map is a map from inode # to new_path.
- map<ino_t, string> hard_links;
- FilesystemIterator iter(copy_source_,
- utils::SetWithValue<string>(kCopyExclusionPrefix));
- bool success = true;
- for (; !g_atomic_int_get(&thread_should_exit_) &&
- !iter.IsEnd(); iter.Increment()) {
- const string old_path = iter.GetFullPath();
- const string new_path = dest_path_ + iter.GetPartialPath();
- LOG(INFO) << "copying " << old_path << " to " << new_path;
- const struct stat stbuf = iter.GetStat();
- success = false;
-
- // Skip lost+found
- CHECK_NE(kCopyExclusionPrefix, iter.GetPartialPath());
-
- // Directories can't be hard-linked, so check for directories first
- if (iter.GetPartialPath().empty()) {
- // Root has an empty path.
- // We don't need to create anything for the root, which is the first
- // thing we get from the iterator.
- success = true;
- } else if (S_ISDIR(stbuf.st_mode)) {
- success = CreateDirSynchronously(new_path, stbuf);
+ 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 {
- if (stbuf.st_nlink > 1 &&
- utils::MapContainsKey(hard_links, stbuf.st_ino)) {
- success = CreateHardLinkSynchronously(hard_links[stbuf.st_ino],
- new_path);
- } else {
- if (stbuf.st_nlink > 1)
- hard_links[stbuf.st_ino] = new_path;
- if (S_ISREG(stbuf.st_mode)) {
- success = CopyFileSynchronously(old_path, new_path, stbuf);
- } else if (S_ISLNK(stbuf.st_mode)) {
- success = CopySymlinkSynchronously(old_path, new_path, stbuf);
- } else if (S_ISFIFO(stbuf.st_mode) ||
- S_ISCHR(stbuf.st_mode) ||
- S_ISBLK(stbuf.st_mode) ||
- S_ISSOCK(stbuf.st_mode)) {
- success = CreateNodeSynchronously(new_path, stbuf);
- } else {
- CHECK(false) << "Unable to copy file " << old_path << " with mode "
- << stbuf.st_mode;
- }
- }
+ LOG(ERROR) << "Write was short: wrote " << bytes_written
+ << " but expected to write " << buffer_valid_size_;
}
- TEST_AND_RETURN_FALSE(success);
-
- // chmod new file
- if (!S_ISLNK(stbuf.st_mode)) {
- int r = chmod(new_path.c_str(), stbuf.st_mode);
- TEST_AND_RETURN_FALSE_ERRNO(r == 0);
- }
-
- // Set uid/gid.
- int r = lchown(new_path.c_str(), stbuf.st_uid, stbuf.st_gid);
- TEST_AND_RETURN_FALSE_ERRNO(r == 0);
+ Cleanup(false, was_cancelled);
+ return;
}
- TEST_AND_RETURN_FALSE(!iter.IsErr());
- // Success!
- return true;
-}
-const char* FilesystemCopierAction::kCompleteFilesystemMarker(
- "/update_engine_copy_success");
+ // 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
« no previous file with comments | « src/platform/update_engine/filesystem_copier_action.h ('k') | src/platform/update_engine/filesystem_copier_action_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698