1 // Copyright 2017 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 "components/zucchini/zucchini_integration.h"
6
7 #include <utility>
8
9 #include "base/logging.h"
10 #include "components/zucchini/buffer_view.h"
11 #include "components/zucchini/mapped_file.h"
12 #include "components/zucchini/patch_reader.h"
13
14 namespace zucchini {
15
16 namespace {
17
18 struct FileNames {
FileNameszucchini::__anon693d23950111::FileNames19 FileNames() : is_dummy(true) {
20 // Use fake names.
21 old_name = old_name.AppendASCII("old_name");
22 new_name = new_name.AppendASCII("new_name");
23 patch_name = patch_name.AppendASCII("patch_name");
24 }
25
FileNameszucchini::__anon693d23950111::FileNames26 FileNames(const base::FilePath& old_name,
27 const base::FilePath& new_name,
28 const base::FilePath& patch_name)
29 : old_name(old_name),
30 new_name(new_name),
31 patch_name(patch_name),
32 is_dummy(false) {}
33
34 base::FilePath old_name;
35 base::FilePath new_name;
36 base::FilePath patch_name;
37
38 // A flag to decide whether the filenames are only for error output.
39 const bool is_dummy;
40 };
41
GenerateCommon(base::File old_file,base::File new_file,base::File patch_file,const FileNames & names,bool force_keep,bool is_raw,std::string imposed_matches)42 status::Code GenerateCommon(base::File old_file,
43 base::File new_file,
44 base::File patch_file,
45 const FileNames& names,
46 bool force_keep,
47 bool is_raw,
48 std::string imposed_matches) {
49 MappedFileReader mapped_old(std::move(old_file));
50 if (mapped_old.HasError()) {
51 LOG(ERROR) << "Error with file " << names.old_name.value() << ": "
52 << mapped_old.error();
53 return status::kStatusFileReadError;
54 }
55
56 MappedFileReader mapped_new(std::move(new_file));
57 if (mapped_new.HasError()) {
58 LOG(ERROR) << "Error with file " << names.new_name.value() << ": "
59 << mapped_new.error();
60 return status::kStatusFileReadError;
61 }
62
63 status::Code result = status::kStatusSuccess;
64 EnsemblePatchWriter patch_writer(mapped_old.region(), mapped_new.region());
65 if (is_raw) {
66 result = GenerateBufferRaw(mapped_old.region(), mapped_new.region(),
67 &patch_writer);
68 } else {
69 result = GenerateBufferImposed(mapped_old.region(), mapped_new.region(),
70 std::move(imposed_matches), &patch_writer);
71 }
72 if (result != status::kStatusSuccess) {
73 LOG(ERROR) << "Fatal error encountered when generating patch.";
74 return result;
75 }
76
77 // By default, delete patch on destruction, to avoid having lingering files in
78 // case of a failure. On Windows deletion can be done by the OS.
79 MappedFileWriter mapped_patch(names.patch_name, std::move(patch_file),
80 patch_writer.SerializedSize());
81 if (mapped_patch.HasError()) {
82 LOG(ERROR) << "Error with file " << names.patch_name.value() << ": "
83 << mapped_patch.error();
84 return status::kStatusFileWriteError;
85 }
86 if (force_keep)
87 mapped_patch.Keep();
88
89 if (!patch_writer.SerializeInto(mapped_patch.region()))
90 return status::kStatusPatchWriteError;
91
92 // Successfully created patch. Explicitly request file to be kept.
93 if (!mapped_patch.Keep())
94 return status::kStatusFileWriteError;
95 return status::kStatusSuccess;
96 }
97
ApplyCommon(base::File old_file,base::File patch_file,base::File new_file,const FileNames & names,bool force_keep)98 status::Code ApplyCommon(base::File old_file,
99 base::File patch_file,
100 base::File new_file,
101 const FileNames& names,
102 bool force_keep) {
103 MappedFileReader mapped_patch(std::move(patch_file));
104 if (mapped_patch.HasError()) {
105 LOG(ERROR) << "Error with file " << names.patch_name.value() << ": "
106 << mapped_patch.error();
107 return status::kStatusFileReadError;
108 }
109
110 auto patch_reader = EnsemblePatchReader::Create(mapped_patch.region());
111 if (!patch_reader.has_value()) {
112 LOG(ERROR) << "Error reading patch header.";
113 return status::kStatusPatchReadError;
114 }
115
116 MappedFileReader mapped_old(std::move(old_file));
117 if (mapped_old.HasError()) {
118 LOG(ERROR) << "Error with file " << names.old_name.value() << ": "
119 << mapped_old.error();
120 return status::kStatusFileReadError;
121 }
122
123 PatchHeader header = patch_reader->header();
124 // By default, delete output on destruction, to avoid having lingering files
125 // in case of a failure. On Windows deletion can be done by the OS.
126 MappedFileWriter mapped_new(names.new_name, std::move(new_file),
127 header.new_size);
128 if (mapped_new.HasError()) {
129 LOG(ERROR) << "Error with file " << names.new_name.value() << ": "
130 << mapped_new.error();
131 return status::kStatusFileWriteError;
132 }
133 if (force_keep)
134 mapped_new.Keep();
135
136 status::Code result =
137 ApplyBuffer(mapped_old.region(), *patch_reader, mapped_new.region());
138 if (result != status::kStatusSuccess) {
139 LOG(ERROR) << "Fatal error encountered while applying patch.";
140 return result;
141 }
142
143 // Successfully patch |mapped_new|. Explicitly request file to be kept.
144 if (!mapped_new.Keep())
145 return status::kStatusFileWriteError;
146 return status::kStatusSuccess;
147 }
148
149 } // namespace
150
Generate(base::File old_file,base::File new_file,base::File patch_file,bool force_keep,bool is_raw,std::string imposed_matches)151 status::Code Generate(base::File old_file,
152 base::File new_file,
153 base::File patch_file,
154 bool force_keep,
155 bool is_raw,
156 std::string imposed_matches) {
157 const FileNames file_names;
158 return GenerateCommon(std::move(old_file), std::move(new_file),
159 std::move(patch_file), file_names, force_keep, is_raw,
160 std::move(imposed_matches));
161 }
162
Generate(const base::FilePath & old_path,const base::FilePath & new_path,const base::FilePath & patch_path,bool force_keep,bool is_raw,std::string imposed_matches)163 status::Code Generate(const base::FilePath& old_path,
164 const base::FilePath& new_path,
165 const base::FilePath& patch_path,
166 bool force_keep,
167 bool is_raw,
168 std::string imposed_matches) {
169 using base::File;
170 File old_file(old_path, File::FLAG_OPEN | File::FLAG_READ);
171 File new_file(new_path, File::FLAG_OPEN | File::FLAG_READ);
172 File patch_file(patch_path, File::FLAG_CREATE_ALWAYS | File::FLAG_READ |
173 File::FLAG_WRITE | File::FLAG_SHARE_DELETE |
174 File::FLAG_CAN_DELETE_ON_CLOSE);
175 const FileNames file_names(old_path, new_path, patch_path);
176 return GenerateCommon(std::move(old_file), std::move(new_file),
177 std::move(patch_file), file_names, force_keep, is_raw,
178 std::move(imposed_matches));
179 }
180
Apply(base::File old_file,base::File patch_file,base::File new_file,bool force_keep)181 status::Code Apply(base::File old_file,
182 base::File patch_file,
183 base::File new_file,
184 bool force_keep) {
185 const FileNames file_names;
186 return ApplyCommon(std::move(old_file), std::move(patch_file),
187 std::move(new_file), file_names, force_keep);
188 }
189
Apply(const base::FilePath & old_path,const base::FilePath & patch_path,const base::FilePath & new_path,bool force_keep)190 status::Code Apply(const base::FilePath& old_path,
191 const base::FilePath& patch_path,
192 const base::FilePath& new_path,
193 bool force_keep) {
194 using base::File;
195 File old_file(old_path, File::FLAG_OPEN | File::FLAG_READ);
196 File patch_file(patch_path, File::FLAG_OPEN | File::FLAG_READ);
197 File new_file(new_path, File::FLAG_CREATE_ALWAYS | File::FLAG_READ |
198 File::FLAG_WRITE | File::FLAG_SHARE_DELETE |
199 File::FLAG_CAN_DELETE_ON_CLOSE);
200 const FileNames file_names(old_path, new_path, patch_path);
201 return ApplyCommon(std::move(old_file), std::move(patch_file),
202 std::move(new_file), file_names, force_keep);
203 }
204
205 } // namespace zucchini
206