Upstream version 7.36.149.0
[platform/framework/web/crosswalk.git] / src / chrome / browser / component_updater / component_patcher_operation.cc
index 6b86d9f..62f7d7b 100644 (file)
@@ -7,6 +7,7 @@
 #include <string>
 #include <vector>
 
+#include "base/bind.h"
 #include "base/file_util.h"
 #include "base/files/memory_mapped_file.h"
 #include "base/json/json_file_value_serializer.h"
 #include "base/strings/string_number_conversions.h"
 #include "chrome/browser/component_updater/component_patcher.h"
 #include "chrome/browser/component_updater/component_updater_service.h"
+#include "chrome/common/chrome_utility_messages.h"
 #include "chrome/common/extensions/extension_constants.h"
+#include "content/public/browser/browser_thread.h"
+#include "content/public/browser/utility_process_host.h"
+#include "courgette/courgette.h"
+#include "courgette/third_party/bsdiff.h"
 #include "crypto/secure_hash.h"
 #include "crypto/sha2.h"
 #include "crypto/signature_verifier.h"
 #include "extensions/common/crx_file.h"
-#include "third_party/zlib/google/zip.h"
+#include "ipc/ipc_message_macros.h"
 
 using crypto::SecureHash;
 
@@ -34,56 +40,166 @@ const char kOutput[] = "output";
 const char kPatch[] = "patch";
 const char kSha256[] = "sha256";
 
+// The integer offset disambiguates between overlapping error ranges.
+const int kCourgetteErrorOffset = 300;
+const int kBsdiffErrorOffset = 600;
+
+class CourgetteTraits : public DeltaUpdateOpPatchStrategy {
+ public:
+  virtual int GetErrorOffset() const OVERRIDE;
+  virtual int GetSuccessCode() const OVERRIDE;
+  virtual scoped_ptr<IPC::Message> GetPatchMessage(
+      base::FilePath input_abs_path,
+      base::FilePath patch_abs_path,
+      base::FilePath output_abs_path) OVERRIDE;
+  virtual int Patch(base::FilePath input_abs_path,
+                    base::FilePath patch_abs_path,
+                    base::FilePath output_abs_path) OVERRIDE;
+};
+
+int CourgetteTraits::GetErrorOffset() const {
+  return kCourgetteErrorOffset;
+}
+
+int CourgetteTraits::GetSuccessCode() const {
+  return courgette::C_OK;
+}
+
+scoped_ptr<IPC::Message> CourgetteTraits::GetPatchMessage(
+    base::FilePath input_abs_path,
+    base::FilePath patch_abs_path,
+    base::FilePath output_abs_path) {
+  return scoped_ptr<IPC::Message>(
+      new ChromeUtilityMsg_PatchFileCourgette(input_abs_path,
+                                              patch_abs_path,
+                                              output_abs_path));
+}
+
+int CourgetteTraits::Patch(base::FilePath input_abs_path,
+                           base::FilePath patch_abs_path,
+                           base::FilePath output_abs_path) {
+  return courgette::ApplyEnsemblePatch(input_abs_path.value().c_str(),
+                                       patch_abs_path.value().c_str(),
+                                       output_abs_path.value().c_str());
+}
+
+class BsdiffTraits : public DeltaUpdateOpPatchStrategy {
+ public:
+  virtual int GetErrorOffset() const OVERRIDE;
+  virtual int GetSuccessCode() const OVERRIDE;
+  virtual scoped_ptr<IPC::Message> GetPatchMessage(
+      base::FilePath input_abs_path,
+      base::FilePath patch_abs_path,
+      base::FilePath output_abs_path) OVERRIDE;
+  virtual int Patch(base::FilePath input_abs_path,
+                    base::FilePath patch_abs_path,
+                    base::FilePath output_abs_path) OVERRIDE;
+};
+
+int BsdiffTraits::GetErrorOffset() const {
+  return kBsdiffErrorOffset;
+}
+
+int BsdiffTraits::GetSuccessCode() const {
+  return courgette::OK;
+}
+
+scoped_ptr<IPC::Message> BsdiffTraits::GetPatchMessage(
+    base::FilePath input_abs_path,
+    base::FilePath patch_abs_path,
+    base::FilePath output_abs_path) {
+  return scoped_ptr<IPC::Message>(
+      new ChromeUtilityMsg_PatchFileBsdiff(input_abs_path,
+                                           patch_abs_path,
+                                           output_abs_path));
+}
+
+int BsdiffTraits::Patch(base::FilePath input_abs_path,
+                        base::FilePath patch_abs_path,
+                        base::FilePath output_abs_path) {
+  return courgette::ApplyBinaryPatch(input_abs_path,
+                                     patch_abs_path,
+                                     output_abs_path);
+}
+
 }  // namespace
 
-DeltaUpdateOp* CreateDeltaUpdateOp(base::DictionaryValue* command) {
-  std::string operation;
-  if (!command->GetString(kOp, &operation))
-    return NULL;
-  if (operation == "copy")
+DeltaUpdateOpPatchStrategy::~DeltaUpdateOpPatchStrategy() {
+}
+
+DeltaUpdateOp* CreateDeltaUpdateOp(const std::string& operation) {
+  if (operation == "copy") {
     return new DeltaUpdateOpCopy();
-  else if (operation == "create")
+  } else if (operation == "create") {
     return new DeltaUpdateOpCreate();
-  else if (operation == "bsdiff")
-    return new DeltaUpdateOpPatchBsdiff();
-  else if (operation == "courgette")
-    return new DeltaUpdateOpPatchCourgette();
+  } else if (operation == "bsdiff") {
+    scoped_ptr<DeltaUpdateOpPatchStrategy> strategy(new BsdiffTraits());
+    return new DeltaUpdateOpPatch(strategy.Pass());
+  } else if (operation == "courgette") {
+    scoped_ptr<DeltaUpdateOpPatchStrategy> strategy(new CourgetteTraits());
+    return new DeltaUpdateOpPatch(strategy.Pass());
+  }
   return NULL;
 }
 
-DeltaUpdateOp::DeltaUpdateOp() {}
+DeltaUpdateOp* CreateDeltaUpdateOp(const base::DictionaryValue& command) {
+  std::string operation;
+  if (!command.GetString(kOp, &operation))
+    return NULL;
+  return CreateDeltaUpdateOp(operation);
+}
+
+DeltaUpdateOp::DeltaUpdateOp() : in_process_(false) {
+}
 
-DeltaUpdateOp::~DeltaUpdateOp() {}
+DeltaUpdateOp::~DeltaUpdateOp() {
+}
 
-ComponentUnpacker::Error DeltaUpdateOp::Run(base::DictionaryValue* command_args,
-                                            const base::FilePath& input_dir,
-                                            const base::FilePath& unpack_dir,
-                                            ComponentPatcher* patcher,
-                                            ComponentInstaller* installer,
-                                            int* error) {
+void DeltaUpdateOp::Run(const base::DictionaryValue* command_args,
+                        const base::FilePath& input_dir,
+                        const base::FilePath& unpack_dir,
+                        ComponentInstaller* installer,
+                        bool in_process,
+                        const ComponentUnpacker::Callback& callback,
+                        scoped_refptr<base::SequencedTaskRunner> task_runner) {
+  callback_ = callback;
+  in_process_ = in_process;
+  task_runner_ = task_runner;
   std::string output_rel_path;
   if (!command_args->GetString(kOutput, &output_rel_path) ||
-      !command_args->GetString(kSha256, &output_sha256_))
-    return ComponentUnpacker::kDeltaBadCommands;
+      !command_args->GetString(kSha256, &output_sha256_)) {
+    DoneRunning(ComponentUnpacker::kDeltaBadCommands, 0);
+    return;
+  }
 
-  output_abs_path_ = unpack_dir.Append(
-      base::FilePath::FromUTF8Unsafe(output_rel_path));
-  ComponentUnpacker::Error parse_result = DoParseArguments(
-      command_args, input_dir, installer);
-  if (parse_result != ComponentUnpacker::kNone)
-    return parse_result;
+  output_abs_path_ =
+      unpack_dir.Append(base::FilePath::FromUTF8Unsafe(output_rel_path));
+  ComponentUnpacker::Error parse_result =
+      DoParseArguments(command_args, input_dir, installer);
+  if (parse_result != ComponentUnpacker::kNone) {
+    DoneRunning(parse_result, 0);
+    return;
+  }
 
   const base::FilePath parent = output_abs_path_.DirName();
   if (!base::DirectoryExists(parent)) {
-    if (!base::CreateDirectory(parent))
-      return ComponentUnpacker::kIoError;
+    if (!base::CreateDirectory(parent)) {
+      DoneRunning(ComponentUnpacker::kIoError, 0);
+      return;
+    }
   }
 
-  ComponentUnpacker::Error run_result = DoRun(patcher, error);
-  if (run_result != ComponentUnpacker::kNone)
-    return run_result;
+  DoRun(base::Bind(&DeltaUpdateOp::DoneRunning,
+                   scoped_refptr<DeltaUpdateOp>(this)));
+}
 
-  return CheckHash();
+void DeltaUpdateOp::DoneRunning(ComponentUnpacker::Error error,
+                                int extended_error) {
+  if (error == ComponentUnpacker::kNone)
+    error = CheckHash();
+  task_runner_->PostTask(FROM_HERE,
+                         base::Bind(callback_, error, extended_error));
+  callback_.Reset();
 }
 
 // Uses the hash as a checksum to confirm that the file now residing in the
@@ -108,10 +224,22 @@ ComponentUnpacker::Error DeltaUpdateOp::CheckHash() {
   return ComponentUnpacker::kNone;
 }
 
-DeltaUpdateOpCopy::DeltaUpdateOpCopy() {}
+bool DeltaUpdateOp::InProcess() {
+  return in_process_;
+}
+
+scoped_refptr<base::SequencedTaskRunner> DeltaUpdateOp::GetTaskRunner() {
+  return task_runner_;
+}
+
+DeltaUpdateOpCopy::DeltaUpdateOpCopy() {
+}
+
+DeltaUpdateOpCopy::~DeltaUpdateOpCopy() {
+}
 
 ComponentUnpacker::Error DeltaUpdateOpCopy::DoParseArguments(
-    base::DictionaryValue* command_args,
+    const base::DictionaryValue* command_args,
     const base::FilePath& input_dir,
     ComponentInstaller* installer) {
   std::string input_rel_path;
@@ -124,76 +252,111 @@ ComponentUnpacker::Error DeltaUpdateOpCopy::DoParseArguments(
   return ComponentUnpacker::kNone;
 }
 
-ComponentUnpacker::Error DeltaUpdateOpCopy::DoRun(ComponentPatcher*,
-                                                  int* error) {
-  *error = 0;
+void DeltaUpdateOpCopy::DoRun(const ComponentUnpacker::Callback& callback) {
   if (!base::CopyFile(input_abs_path_, output_abs_path_))
-    return ComponentUnpacker::kDeltaOperationFailure;
+    callback.Run(ComponentUnpacker::kDeltaOperationFailure, 0);
+  else
+    callback.Run(ComponentUnpacker::kNone, 0);
+}
 
-  return ComponentUnpacker::kNone;
+DeltaUpdateOpCreate::DeltaUpdateOpCreate() {
 }
 
-DeltaUpdateOpCreate::DeltaUpdateOpCreate() {}
+DeltaUpdateOpCreate::~DeltaUpdateOpCreate() {
+}
 
 ComponentUnpacker::Error DeltaUpdateOpCreate::DoParseArguments(
-    base::DictionaryValue* command_args,
+    const base::DictionaryValue* command_args,
     const base::FilePath& input_dir,
     ComponentInstaller* installer) {
   std::string patch_rel_path;
   if (!command_args->GetString(kPatch, &patch_rel_path))
     return ComponentUnpacker::kDeltaBadCommands;
 
-  patch_abs_path_ = input_dir.Append(
-      base::FilePath::FromUTF8Unsafe(patch_rel_path));
+  patch_abs_path_ =
+      input_dir.Append(base::FilePath::FromUTF8Unsafe(patch_rel_path));
 
   return ComponentUnpacker::kNone;
 }
 
-ComponentUnpacker::Error DeltaUpdateOpCreate::DoRun(ComponentPatcher*,
-                                                    int* error) {
-  *error = 0;
+void DeltaUpdateOpCreate::DoRun(const ComponentUnpacker::Callback& callback) {
   if (!base::Move(patch_abs_path_, output_abs_path_))
-    return ComponentUnpacker::kDeltaOperationFailure;
+    callback.Run(ComponentUnpacker::kDeltaOperationFailure, 0);
+  else
+    callback.Run(ComponentUnpacker::kNone, 0);
+}
 
-  return ComponentUnpacker::kNone;
+DeltaUpdateOpPatchHost::DeltaUpdateOpPatchHost(
+    scoped_refptr<DeltaUpdateOpPatch> patcher,
+    scoped_refptr<base::SequencedTaskRunner> task_runner)
+    : patcher_(patcher), task_runner_(task_runner) {
 }
 
-DeltaUpdateOpPatchBsdiff::DeltaUpdateOpPatchBsdiff() {}
+DeltaUpdateOpPatchHost::~DeltaUpdateOpPatchHost() {
+}
 
-ComponentUnpacker::Error DeltaUpdateOpPatchBsdiff::DoParseArguments(
-    base::DictionaryValue* command_args,
-    const base::FilePath& input_dir,
-    ComponentInstaller* installer) {
-  std::string patch_rel_path;
-  std::string input_rel_path;
-  if (!command_args->GetString(kPatch, &patch_rel_path) ||
-      !command_args->GetString(kInput, &input_rel_path))
-    return ComponentUnpacker::kDeltaBadCommands;
+void DeltaUpdateOpPatchHost::StartProcess(scoped_ptr<IPC::Message> message) {
+  // The DeltaUpdateOpPatchHost is not responsible for deleting the
+  // UtilityProcessHost object.
+  content::UtilityProcessHost* host = content::UtilityProcessHost::Create(
+      this, base::MessageLoopProxy::current().get());
+  host->DisableSandbox();
+  host->Send(message.release());
+}
 
-  if (!installer->GetInstalledFile(input_rel_path, &input_abs_path_))
-    return ComponentUnpacker::kDeltaMissingExistingFile;
+bool DeltaUpdateOpPatchHost::OnMessageReceived(const IPC::Message& message) {
+  bool handled = true;
+  IPC_BEGIN_MESSAGE_MAP(DeltaUpdateOpPatchHost, message)
+    IPC_MESSAGE_HANDLER(ChromeUtilityHostMsg_PatchFile_Succeeded,
+                        OnPatchSucceeded)
+    IPC_MESSAGE_HANDLER(ChromeUtilityHostMsg_PatchFile_Failed,
+                        OnPatchFailed)
+    IPC_MESSAGE_UNHANDLED(handled = false)
+  IPC_END_MESSAGE_MAP()
+  return handled;
+}
+
+void DeltaUpdateOpPatchHost::OnPatchSucceeded() {
+  task_runner_->PostTask(FROM_HERE,
+                         base::Bind(&DeltaUpdateOpPatch::DonePatching,
+                                    patcher_,
+                                    ComponentUnpacker::kNone,
+                                    0));
+  task_runner_ = NULL;
+  patcher_ = NULL;
+}
 
-  patch_abs_path_ = input_dir.Append(
-      base::FilePath::FromUTF8Unsafe(patch_rel_path));
+void DeltaUpdateOpPatchHost::OnPatchFailed(int error_code) {
+  task_runner_->PostTask(FROM_HERE,
+                         base::Bind(&DeltaUpdateOpPatch::DonePatching,
+                                    patcher_,
+                                    ComponentUnpacker::kDeltaOperationFailure,
+                                    error_code));
+  task_runner_ = NULL;
+  patcher_ = NULL;
+}
 
-  return ComponentUnpacker::kNone;
+void DeltaUpdateOpPatchHost::OnProcessCrashed(int exit_code) {
+  task_runner_->PostTask(
+      FROM_HERE,
+      base::Bind(&DeltaUpdateOpPatch::DonePatching,
+                 patcher_,
+                 ComponentUnpacker::kDeltaPatchProcessFailure,
+                 exit_code));
+  task_runner_ = NULL;
+  patcher_ = NULL;
 }
 
-ComponentUnpacker::Error DeltaUpdateOpPatchBsdiff::DoRun(
-    ComponentPatcher* patcher,
-    int* error) {
-  *error = 0;
-  return patcher->Patch(ComponentPatcher::kPatchTypeBsdiff,
-                        input_abs_path_,
-                        patch_abs_path_,
-                        output_abs_path_,
-                        error);
+DeltaUpdateOpPatch::DeltaUpdateOpPatch(
+    scoped_ptr<DeltaUpdateOpPatchStrategy> strategy) {
+  strategy_ = strategy.Pass();
 }
 
-DeltaUpdateOpPatchCourgette::DeltaUpdateOpPatchCourgette() {}
+DeltaUpdateOpPatch::~DeltaUpdateOpPatch() {
+}
 
-ComponentUnpacker::Error DeltaUpdateOpPatchCourgette::DoParseArguments(
-    base::DictionaryValue* command_args,
+ComponentUnpacker::Error DeltaUpdateOpPatch::DoParseArguments(
+    const base::DictionaryValue* command_args,
     const base::FilePath& input_dir,
     ComponentInstaller* installer) {
   std::string patch_rel_path;
@@ -205,21 +368,46 @@ ComponentUnpacker::Error DeltaUpdateOpPatchCourgette::DoParseArguments(
   if (!installer->GetInstalledFile(input_rel_path, &input_abs_path_))
     return ComponentUnpacker::kDeltaMissingExistingFile;
 
-  patch_abs_path_ = input_dir.Append(
-      base::FilePath::FromUTF8Unsafe(patch_rel_path));
+  patch_abs_path_ =
+      input_dir.Append(base::FilePath::FromUTF8Unsafe(patch_rel_path));
 
   return ComponentUnpacker::kNone;
 }
 
-ComponentUnpacker::Error DeltaUpdateOpPatchCourgette::DoRun(
-    ComponentPatcher* patcher,
-    int* error) {
-  *error = 0;
-  return patcher->Patch(ComponentPatcher::kPatchTypeCourgette,
-                        input_abs_path_,
-                        patch_abs_path_,
-                        output_abs_path_,
-                        error);
+void DeltaUpdateOpPatch::DoRun(const ComponentUnpacker::Callback& callback) {
+  callback_ = callback;
+  if (!InProcess()) {
+    host_ = new DeltaUpdateOpPatchHost(scoped_refptr<DeltaUpdateOpPatch>(this),
+                                       GetTaskRunner());
+    content::BrowserThread::PostTask(
+        content::BrowserThread::IO,
+        FROM_HERE,
+        base::Bind(&DeltaUpdateOpPatchHost::StartProcess,
+                   host_,
+                   base::Passed(strategy_->GetPatchMessage(input_abs_path_,
+                                                           patch_abs_path_,
+                                                           output_abs_path_))));
+    return;
+  }
+  const int result = strategy_->Patch(input_abs_path_,
+                                      patch_abs_path_,
+                                      output_abs_path_);
+  if (result == strategy_->GetSuccessCode())
+    DonePatching(ComponentUnpacker::kNone, 0);
+  else
+    DonePatching(ComponentUnpacker::kDeltaOperationFailure, result);
+}
+
+void DeltaUpdateOpPatch::DonePatching(ComponentUnpacker::Error error,
+                                      int error_code) {
+  host_ = NULL;
+  if (error != ComponentUnpacker::kNone) {
+    error_code += strategy_->GetErrorOffset();
+  }
+  callback_.Run(error, error_code);
+  // The callback is no longer needed - it is best to release it in case it
+  // contains a reference to this object.
+  callback_.Reset();
 }
 
 }  // namespace component_updater