archive_patch_helper.cc revision ca12bfac764ba476d6cd062bf1dde12cc64c3f40
1// Copyright 2013 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "chrome/installer/setup/archive_patch_helper.h"
6
7#include "base/file_util.h"
8#include "base/logging.h"
9#include "chrome/installer/util/lzma_util.h"
10#include "courgette/courgette.h"
11#include "third_party/bspatch/mbspatch.h"
12
13namespace installer {
14
15ArchivePatchHelper::ArchivePatchHelper(const base::FilePath& working_directory,
16                                       const base::FilePath& compressed_archive,
17                                       const base::FilePath& patch_source,
18                                       const base::FilePath& target)
19    : working_directory_(working_directory),
20      compressed_archive_(compressed_archive),
21      patch_source_(patch_source),
22      target_(target) {}
23
24ArchivePatchHelper::~ArchivePatchHelper() {}
25
26// static
27bool ArchivePatchHelper::UncompressAndPatch(
28    const base::FilePath& working_directory,
29    const base::FilePath& compressed_archive,
30    const base::FilePath& patch_source,
31    const base::FilePath& target) {
32  ArchivePatchHelper instance(working_directory, compressed_archive,
33                              patch_source, target);
34  return (instance.Uncompress(NULL) &&
35          (instance.EnsemblePatch() || instance.BinaryPatch()));
36}
37
38bool ArchivePatchHelper::Uncompress(base::FilePath* last_uncompressed_file) {
39  // The target shouldn't already exist.
40  DCHECK(!base::PathExists(target_));
41
42  // UnPackArchive takes care of logging.
43  string16 output_file;
44  int32 lzma_result = LzmaUtil::UnPackArchive(compressed_archive_.value(),
45                                              working_directory_.value(),
46                                              &output_file);
47  if (lzma_result != NO_ERROR)
48    return false;
49
50  last_uncompressed_file_ = base::FilePath(output_file);
51  if (last_uncompressed_file)
52    *last_uncompressed_file = last_uncompressed_file_;
53  return true;
54}
55
56bool ArchivePatchHelper::EnsemblePatch() {
57  if (last_uncompressed_file_.empty()) {
58    LOG(ERROR) << "No patch file found in compressed archive.";
59    return false;
60  }
61
62  courgette::Status result =
63      courgette::ApplyEnsemblePatch(patch_source_.value().c_str(),
64                                    last_uncompressed_file_.value().c_str(),
65                                    target_.value().c_str());
66  if (result == courgette::C_OK)
67    return true;
68
69  LOG(ERROR)
70      << "Failed to apply patch " << last_uncompressed_file_.value()
71      << " to file " << patch_source_.value()
72      << " and generating file " << target_.value()
73      << " using courgette. err=" << result;
74
75  // Ensure a partial output is not left behind.
76  base::DeleteFile(target_, false);
77
78  return false;
79}
80
81bool ArchivePatchHelper::BinaryPatch() {
82  if (last_uncompressed_file_.empty()) {
83    LOG(ERROR) << "No patch file found in compressed archive.";
84    return false;
85  }
86
87  int result = ApplyBinaryPatch(patch_source_.value().c_str(),
88                                last_uncompressed_file_.value().c_str(),
89                                target_.value().c_str());
90  if (result == OK)
91    return true;
92
93  LOG(ERROR)
94      << "Failed to apply patch " << last_uncompressed_file_.value()
95      << " to file " << patch_source_.value()
96      << " and generating file " << target_.value()
97      << " using bsdiff. err=" << result;
98
99  // Ensure a partial output is not left behind.
100  base::DeleteFile(target_, false);
101
102  return false;
103}
104
105}  // namespace installer
106