AU: FilesystemCopierAction: copy bit-exactly

Review URL: http://codereview.chromium.org/1700018
diff --git a/filesystem_copier_action.h b/filesystem_copier_action.h
index 3a330ef..9e7f060 100644
--- a/filesystem_copier_action.h
+++ b/filesystem_copier_action.h
@@ -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.
 
@@ -8,30 +8,14 @@
 #include <sys/stat.h>
 #include <sys/types.h>
 #include <string>
+#include <vector>
+#include <gio/gio.h>
 #include <glib.h>
 #include "update_engine/action.h"
 #include "update_engine/install_plan.h"
 
 // This action will only do real work if it's a delta update. It will
-// format the install partition as ext3/4, copy the root filesystem into it,
-// and then terminate.
-
-// Implementation notes: This action uses a helper thread, which seems to
-// violate the design decision to only have a single thread and use
-// asynchronous i/o. The issue is that (to the best of my knowledge),
-// there are no linux APIs to crawl a filesystem's metadata asynchronously.
-// The suggested way seems to be to open the raw device and parse the ext
-// filesystem. That's not a good approach for a number of reasons:
-// - ties us to ext filesystem
-// - although this wouldn't happen at the time of writing, it may not handle
-//   changes to the source fs during the copy as gracefully.
-// - requires us to have read-access to the source filesystem device, which
-//   may be a security issue.
-//
-// Having said this, using a helper thread is not ideal, but it's acceptable:
-// we still honor the Action API. That is, all interaction between the action
-// and other objects in the system (e.g. the ActionProcessor) happens on the
-// main thread. The helper thread is fully encapsulated by the action.
+// copy the root partition to install partition, and then terminate.
 
 namespace chromeos_update_engine {
 
@@ -49,10 +33,11 @@
 class FilesystemCopierAction : public Action<FilesystemCopierAction> {
  public:
   FilesystemCopierAction()
-      : thread_should_exit_(0),
-        is_mounted_(false),
-        copy_source_("/"),
-        skipped_copy_(false) {}
+      : src_stream_(NULL),
+        dst_stream_(NULL),
+        canceller_(NULL),
+        read_in_flight_(false),
+        buffer_valid_size_(0) {}
   typedef ActionTraits<FilesystemCopierAction>::InputObjectType
   InputObjectType;
   typedef ActionTraits<FilesystemCopierAction>::OutputObjectType
@@ -64,81 +49,51 @@
   void set_copy_source(const std::string& path) {
     copy_source_ = path;
   }
-  // Returns true if we detected that a copy was unneeded and thus skipped it.
-  bool skipped_copy() { return skipped_copy_; }
 
   // Debugging/logging
   static std::string StaticType() { return "FilesystemCopierAction"; }
   std::string Type() const { return StaticType(); }
 
  private:
-  // These synchronously mount or unmount the given mountpoint
-  bool Mount(const std::string& device, const std::string& mountpoint);
-  bool Unmount(const std::string& mountpoint);
-
-  // Performs a recursive file/directory copy from copy_source_ to dest_path_.
-  // Doesn't return until the copy has completed. Returns true on success
-  // or false on error.
-  bool CopySynchronously();
-
-  // There are helper functions for CopySynchronously. They handle creating
-  // various types of files. They return true on success.
-  bool CreateDirSynchronously(const std::string& new_path,
-                              const struct stat& stbuf);
-  bool CopyFileSynchronously(const std::string& old_path,
-                             const std::string& new_path,
-                             const struct stat& stbuf);
-  bool CreateHardLinkSynchronously(const std::string& old_path,
-                                   const std::string& new_path);
-  // Note: Here, old_path is an existing symlink that will be copied to
-  // new_path. Thus, old_path is *not* the same as the old_path from
-  // the symlink() syscall.
-  bool CopySymlinkSynchronously(const std::string& old_path,
-                                const std::string& new_path,
-                                const struct stat& stbuf);
-  bool CreateNodeSynchronously(const std::string& new_path,
-                               const struct stat& stbuf);
-
-  // Returns NULL on success
-  void* HelperThreadMain();
-  static void* HelperThreadMainStatic(void* data) {
-    FilesystemCopierAction* self =
-        reinterpret_cast<FilesystemCopierAction*>(data);
-    return self->HelperThreadMain();
+  // Callback from glib when the copy operation is done.
+  void AsyncReadyCallback(GObject *source_object, GAsyncResult *res);
+  static void StaticAsyncReadyCallback(GObject *source_object,
+                                       GAsyncResult *res,
+                                       gpointer user_data) {
+    reinterpret_cast<FilesystemCopierAction*>(user_data)->AsyncReadyCallback(
+        source_object, res);
   }
-
-  // Joins the thread and tells the processor that we're done
-  void CollectThread();
-  // GMainLoop callback function:
-  static gboolean CollectThreadStatic(gpointer data) {
-    FilesystemCopierAction* self =
-        reinterpret_cast<FilesystemCopierAction*>(data);
-    self->CollectThread();
-    return FALSE;
-  }
-
-  pthread_t helper_thread_;
-
-  volatile gint thread_should_exit_;
-
-  static const char* kCompleteFilesystemMarker;
-
-  // Whether or not the destination device is currently mounted.
-  bool is_mounted_;
-
-  // Where the destination device is mounted.
-  std::string dest_path_;
-
-  // The path to copy from. Usually left as the default "/", but tests can
-  // change it.
+  
+  // Cleans up all the variables we use for async operations and tells
+  // the ActionProcessor we're done w/ success as passed in.
+  // was_cancelled should be true if TerminateProcessing() was called.
+  void Cleanup(bool success, bool was_cancelled);
+  
+  // The path to copy from. If empty (the default), the source is from the
+  // passed in InstallPlan.
   std::string copy_source_;
 
+  // If non-NULL, these are GUnixInputStream objects for the opened
+  // source/destination partitions.
+  GInputStream* src_stream_;
+  GOutputStream* dst_stream_;
+  
+  // If non-NULL, the cancellable object for the in-flight async call.
+  GCancellable* canceller_;
+  
+  // True if we're waiting on a read to complete; false if we're
+  // waiting on a write.
+  bool read_in_flight_;
+  
+  // The buffer for storing data we read/write.
+  std::vector<char> buffer_;
+
+  // Number of valid elements in buffer_.
+  std::vector<char>::size_type buffer_valid_size_;
+
   // The install plan we're passed in via the input pipe.
   InstallPlan install_plan_;
-
-  // Set to true if we detected the copy was unneeded and thus we skipped it.
-  bool skipped_copy_;
-
+  
   DISALLOW_COPY_AND_ASSIGN(FilesystemCopierAction);
 };