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