aboutsummaryrefslogtreecommitdiff
path: root/zucchini_integration.cc
blob: 3ca4601bcab001267dc81411f89fa9449bd1b688 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
// Copyright 2017 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 "components/zucchini/zucchini_integration.h"

#include <utility>

#include "base/logging.h"
#include "components/zucchini/buffer_view.h"
#include "components/zucchini/mapped_file.h"
#include "components/zucchini/patch_reader.h"

namespace zucchini {

namespace {

struct FileNames {
  FileNames() : is_dummy(true) {
    // Use fake names. If |is_dummy| is true these files are only used for error
    // output.
    old_name = old_name.AppendASCII("old_name");
    patch_name = patch_name.AppendASCII("patch_name");
    new_name = new_name.AppendASCII("new_name");
  }

  FileNames(const base::FilePath& old_name,
            const base::FilePath& patch_name,
            const base::FilePath& new_name)
      : old_name(old_name),
        patch_name(patch_name),
        new_name(new_name),
        is_dummy(false) {}

  base::FilePath old_name;
  base::FilePath patch_name;
  base::FilePath new_name;
  const bool is_dummy;
};

status::Code ApplyCommon(base::File&& old_file_handle,
                         base::File&& patch_file_handle,
                         base::File&& new_file_handle,
                         const FileNames& names) {
  MappedFileReader patch_file(std::move(patch_file_handle));
  if (patch_file.HasError()) {
    LOG(ERROR) << "Error with file " << names.patch_name.value() << ": "
               << patch_file.error();
    return status::kStatusFileReadError;
  }

  auto patch_reader =
      zucchini::EnsemblePatchReader::Create(patch_file.region());
  if (!patch_reader.has_value()) {
    LOG(ERROR) << "Error reading patch header.";
    return status::kStatusPatchReadError;
  }

  MappedFileReader old_file(std::move(old_file_handle));
  if (old_file.HasError()) {
    LOG(ERROR) << "Error with file " << names.old_name.value() << ": "
               << old_file.error();
    return status::kStatusFileReadError;
  }
  if (!patch_reader->CheckOldFile(old_file.region())) {
    LOG(ERROR) << "Invalid old_file.";
    return status::kStatusInvalidOldImage;
  }

  zucchini::PatchHeader header = patch_reader->header();
  // By default, delete output on destruction, to avoid having lingering files
  // in case of a failure. On Windows deletion can be done by the OS.
  base::FilePath file_path;
  if (!names.is_dummy)
    file_path = base::FilePath(names.new_name);

  MappedFileWriter new_file(file_path, std::move(new_file_handle),
                            header.new_size);
  if (new_file.HasError()) {
    LOG(ERROR) << "Error with file " << names.new_name.value() << ": "
               << new_file.error();
    return status::kStatusFileWriteError;
  }

  zucchini::status::Code result =
      zucchini::Apply(old_file.region(), *patch_reader, new_file.region());
  if (result != status::kStatusSuccess) {
    LOG(ERROR) << "Fatal error encountered while applying patch.";
    return result;
  }

  // Successfully patch |new_file|. Explicitly request file to be kept.
  if (!new_file.Keep())
    return status::kStatusFileWriteError;
  return status::kStatusSuccess;
}

}  // namespace

status::Code Apply(base::File old_file_handle,
                   base::File patch_file_handle,
                   base::File new_file_handle) {
  const FileNames file_names = FileNames();
  return ApplyCommon(std::move(old_file_handle), std::move(patch_file_handle),
                     std::move(new_file_handle), file_names);
}

status::Code Apply(const base::FilePath& old_path,
                   const base::FilePath& patch_path,
                   const base::FilePath& new_path) {
  using base::File;
  File old_file(old_path, File::FLAG_OPEN | File::FLAG_READ);
  File patch_file(patch_path, File::FLAG_OPEN | File::FLAG_READ);
  File new_file(new_path, File::FLAG_CREATE_ALWAYS | File::FLAG_READ |
                              File::FLAG_WRITE | File::FLAG_SHARE_DELETE |
                              File::FLAG_CAN_DELETE_ON_CLOSE);
  const FileNames file_names(old_path, patch_path, new_path);
  return ApplyCommon(std::move(old_file), std::move(patch_file),
                     std::move(new_file), file_names);
}

}  // namespace zucchini