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::__anondc9f2f870111::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::__anondc9f2f870111::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
VerifyPatchCommon(base::File patch_file,base::FilePath patch_name)149 status::Code VerifyPatchCommon(base::File patch_file,
150 base::FilePath patch_name) {
151 MappedFileReader mapped_patch(std::move(patch_file));
152 if (mapped_patch.HasError()) {
153 LOG(ERROR) << "Error with file " << patch_name.value() << ": "
154 << mapped_patch.error();
155 return status::kStatusFileReadError;
156 }
157 auto patch_reader = EnsemblePatchReader::Create(mapped_patch.region());
158 if (!patch_reader.has_value()) {
159 LOG(ERROR) << "Error reading patch header.";
160 return status::kStatusPatchReadError;
161 }
162 return status::kStatusSuccess;
163 }
164
165 } // namespace
166
Generate(base::File old_file,base::File new_file,base::File patch_file,bool force_keep,bool is_raw,std::string imposed_matches)167 status::Code Generate(base::File old_file,
168 base::File new_file,
169 base::File patch_file,
170 bool force_keep,
171 bool is_raw,
172 std::string imposed_matches) {
173 const FileNames file_names;
174 return GenerateCommon(std::move(old_file), std::move(new_file),
175 std::move(patch_file), file_names, force_keep, is_raw,
176 std::move(imposed_matches));
177 }
178
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)179 status::Code Generate(const base::FilePath& old_path,
180 const base::FilePath& new_path,
181 const base::FilePath& patch_path,
182 bool force_keep,
183 bool is_raw,
184 std::string imposed_matches) {
185 using base::File;
186 File old_file(old_path, File::FLAG_OPEN | File::FLAG_READ |
187 base::File::FLAG_WIN_SHARE_DELETE);
188 File new_file(new_path, File::FLAG_OPEN | File::FLAG_READ |
189 base::File::FLAG_WIN_SHARE_DELETE);
190 File patch_file(patch_path, File::FLAG_CREATE_ALWAYS | File::FLAG_READ |
191 File::FLAG_WRITE |
192 File::FLAG_WIN_SHARE_DELETE |
193 File::FLAG_CAN_DELETE_ON_CLOSE);
194 const FileNames file_names(old_path, new_path, patch_path);
195 return GenerateCommon(std::move(old_file), std::move(new_file),
196 std::move(patch_file), file_names, force_keep, is_raw,
197 std::move(imposed_matches));
198 }
199
Apply(base::File old_file,base::File patch_file,base::File new_file,bool force_keep)200 status::Code Apply(base::File old_file,
201 base::File patch_file,
202 base::File new_file,
203 bool force_keep) {
204 const FileNames file_names;
205 return ApplyCommon(std::move(old_file), std::move(patch_file),
206 std::move(new_file), file_names, force_keep);
207 }
208
Apply(const base::FilePath & old_path,const base::FilePath & patch_path,const base::FilePath & new_path,bool force_keep)209 status::Code Apply(const base::FilePath& old_path,
210 const base::FilePath& patch_path,
211 const base::FilePath& new_path,
212 bool force_keep) {
213 using base::File;
214 File old_file(old_path, File::FLAG_OPEN | File::FLAG_READ |
215 base::File::FLAG_WIN_SHARE_DELETE);
216 File patch_file(patch_path, File::FLAG_OPEN | File::FLAG_READ |
217 base::File::FLAG_WIN_SHARE_DELETE);
218 File new_file(new_path, File::FLAG_CREATE_ALWAYS | File::FLAG_READ |
219 File::FLAG_WRITE | File::FLAG_WIN_SHARE_DELETE |
220 File::FLAG_CAN_DELETE_ON_CLOSE);
221 const FileNames file_names(old_path, new_path, patch_path);
222 return ApplyCommon(std::move(old_file), std::move(patch_file),
223 std::move(new_file), file_names, force_keep);
224 }
225
VerifyPatch(base::File patch_file)226 status::Code VerifyPatch(base::File patch_file) {
227 return VerifyPatchCommon(std::move(patch_file), base::FilePath());
228 }
229
VerifyPatch(const base::FilePath & patch_path)230 status::Code VerifyPatch(const base::FilePath& patch_path) {
231 using base::File;
232 File patch_file(patch_path, File::FLAG_OPEN | File::FLAG_READ |
233 base::File::FLAG_SHARE_DELETE);
234 return VerifyPatchCommon(std::move(patch_file), patch_path);
235 }
236
237 } // namespace zucchini
238