1 // Copyright (c) 2011 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.
8 #include "base/at_exit.h"
9 #include "base/basictypes.h"
10 #include "base/command_line.h"
11 #include "base/file_path.h"
12 #include "base/file_util.h"
13 #include "base/logging.h"
14 #include "base/string_number_conversions.h"
15 #include "base/string_util.h"
16 #include "base/utf_string_conversions.h"
17 #include "courgette/third_party/bsdiff.h"
18 #include "courgette/courgette.h"
19 #include "courgette/streams.h"
25 " courgette -supported <executable_file>\n"
26 " courgette -dis <executable_file> <binary_assembly_file>\n"
27 " courgette -asm <binary_assembly_file> <executable_file>\n"
28 " courgette -disadj <executable_file> <reference> <binary_assembly_file>\n"
29 " courgette -gen <v1> <v2> <patch>\n"
30 " courgette -apply <v1> <patch> <v2>\n"
34 void UsageProblem(const char* message
) {
35 fprintf(stderr
, "%s", message
);
36 fprintf(stderr
, "\n");
41 void Problem(const char* format
, ...) {
43 va_start(args
, format
);
44 vfprintf(stderr
, format
, args
);
45 fprintf(stderr
, "\n");
50 std::string
ReadOrFail(const base::FilePath
& file_name
, const char* kind
) {
52 if (!file_util::GetFileSize(file_name
, &file_size
))
53 Problem("Can't read %s file.", kind
);
55 buffer
.reserve(static_cast<size_t>(file_size
));
56 if (!file_util::ReadFileToString(file_name
, &buffer
))
57 Problem("Can't read %s file.", kind
);
61 void WriteSinkToFile(const courgette::SinkStream
*sink
,
62 const base::FilePath
& output_file
) {
64 file_util::WriteFile(output_file
,
65 reinterpret_cast<const char*>(sink
->Buffer()),
66 static_cast<int>(sink
->Length()));
68 Problem("Can't write output.");
69 if (static_cast<size_t>(count
) != sink
->Length())
70 Problem("Incomplete write.");
73 void Disassemble(const base::FilePath
& input_file
,
74 const base::FilePath
& output_file
) {
75 std::string buffer
= ReadOrFail(input_file
, "input");
77 courgette::AssemblyProgram
* program
= NULL
;
78 const courgette::Status parse_status
=
79 courgette::ParseDetectedExecutable(buffer
.c_str(), buffer
.length(),
82 if (parse_status
!= courgette::C_OK
)
83 Problem("Can't parse input.");
85 courgette::EncodedProgram
* encoded
= NULL
;
86 const courgette::Status encode_status
= Encode(program
, &encoded
);
88 courgette::DeleteAssemblyProgram(program
);
90 if (encode_status
!= courgette::C_OK
)
91 Problem("Can't encode program.");
93 courgette::SinkStreamSet sinks
;
95 const courgette::Status write_status
=
96 courgette::WriteEncodedProgram(encoded
, &sinks
);
97 if (write_status
!= courgette::C_OK
)
98 Problem("Can't serialize encoded program.");
100 courgette::DeleteEncodedProgram(encoded
);
102 courgette::SinkStream sink
;
103 if (!sinks
.CopyTo(&sink
))
104 Problem("Can't combine serialized encoded program streams.");
106 WriteSinkToFile(&sink
, output_file
);
109 bool Supported(const base::FilePath
& input_file
) {
112 std::string buffer
= ReadOrFail(input_file
, "input");
114 courgette::ExecutableType type
;
115 size_t detected_length
;
117 DetectExecutableType(buffer
.c_str(), buffer
.length(),
121 // If the detection fails, we just fall back on UNKNOWN
122 std::string format
= "Unsupported";
126 case courgette::EXE_UNKNOWN
:
129 case courgette::EXE_WIN_32_X86
:
130 format
= "Windows 32 PE";
134 case courgette::EXE_ELF_32_X86
:
135 format
= "ELF 32 X86";
140 printf("%s Executable\n", format
.c_str());
144 void DisassembleAndAdjust(const base::FilePath
& program_file
,
145 const base::FilePath
& model_file
,
146 const base::FilePath
& output_file
) {
147 std::string program_buffer
= ReadOrFail(program_file
, "program");
148 std::string model_buffer
= ReadOrFail(model_file
, "reference");
150 courgette::AssemblyProgram
* program
= NULL
;
151 const courgette::Status parse_program_status
=
152 courgette::ParseDetectedExecutable(program_buffer
.c_str(),
153 program_buffer
.length(),
155 if (parse_program_status
!= courgette::C_OK
)
156 Problem("Can't parse program input.");
158 courgette::AssemblyProgram
* model
= NULL
;
159 const courgette::Status parse_model_status
=
160 courgette::ParseDetectedExecutable(model_buffer
.c_str(),
161 model_buffer
.length(),
163 if (parse_model_status
!= courgette::C_OK
)
164 Problem("Can't parse model input.");
166 const courgette::Status adjust_status
= Adjust(*model
, program
);
167 if (adjust_status
!= courgette::C_OK
)
168 Problem("Can't adjust program.");
170 courgette::EncodedProgram
* encoded
= NULL
;
171 const courgette::Status encode_status
= Encode(program
, &encoded
);
173 courgette::DeleteAssemblyProgram(program
);
175 if (encode_status
!= courgette::C_OK
)
176 Problem("Can't encode program.");
178 courgette::SinkStreamSet sinks
;
180 const courgette::Status write_status
=
181 courgette::WriteEncodedProgram(encoded
, &sinks
);
182 if (write_status
!= courgette::C_OK
)
183 Problem("Can't serialize encoded program.");
185 courgette::DeleteEncodedProgram(encoded
);
187 courgette::SinkStream sink
;
188 if (!sinks
.CopyTo(&sink
))
189 Problem("Can't combine serialized encoded program streams.");
191 WriteSinkToFile(&sink
, output_file
);
194 // Diffs two executable files, write a set of files for the diff, one file per
195 // stream of the EncodedProgram format. Each file is the bsdiff between the
196 // original file's stream and the new file's stream. This is completely
197 // uninteresting to users, but it is handy for seeing how much each which
198 // streams are contributing to the final file size. Adjustment is optional.
199 void DisassembleAdjustDiff(const base::FilePath
& model_file
,
200 const base::FilePath
& program_file
,
201 const base::FilePath
& output_file_root
,
203 std::string model_buffer
= ReadOrFail(model_file
, "'old'");
204 std::string program_buffer
= ReadOrFail(program_file
, "'new'");
206 courgette::AssemblyProgram
* model
= NULL
;
207 const courgette::Status parse_model_status
=
208 courgette::ParseDetectedExecutable(model_buffer
.c_str(),
209 model_buffer
.length(),
211 if (parse_model_status
!= courgette::C_OK
)
212 Problem("Can't parse model input.");
214 courgette::AssemblyProgram
* program
= NULL
;
215 const courgette::Status parse_program_status
=
216 courgette::ParseDetectedExecutable(program_buffer
.c_str(),
217 program_buffer
.length(),
219 if (parse_program_status
!= courgette::C_OK
)
220 Problem("Can't parse program input.");
223 const courgette::Status adjust_status
= Adjust(*model
, program
);
224 if (adjust_status
!= courgette::C_OK
)
225 Problem("Can't adjust program.");
228 courgette::EncodedProgram
* encoded_program
= NULL
;
229 const courgette::Status encode_program_status
=
230 Encode(program
, &encoded_program
);
231 courgette::DeleteAssemblyProgram(program
);
232 if (encode_program_status
!= courgette::C_OK
)
233 Problem("Can't encode program.");
235 courgette::EncodedProgram
* encoded_model
= NULL
;
236 const courgette::Status encode_model_status
= Encode(model
, &encoded_model
);
237 courgette::DeleteAssemblyProgram(model
);
238 if (encode_model_status
!= courgette::C_OK
)
239 Problem("Can't encode model.");
241 courgette::SinkStreamSet program_sinks
;
242 const courgette::Status write_program_status
=
243 courgette::WriteEncodedProgram(encoded_program
, &program_sinks
);
244 if (write_program_status
!= courgette::C_OK
)
245 Problem("Can't serialize encoded program.");
246 courgette::DeleteEncodedProgram(encoded_program
);
248 courgette::SinkStreamSet model_sinks
;
249 const courgette::Status write_model_status
=
250 courgette::WriteEncodedProgram(encoded_model
, &model_sinks
);
251 if (write_model_status
!= courgette::C_OK
)
252 Problem("Can't serialize encoded model.");
253 courgette::DeleteEncodedProgram(encoded_model
);
255 courgette::SinkStream empty_sink
;
256 for (int i
= 0; ; ++i
) {
257 courgette::SinkStream
* old_stream
= model_sinks
.stream(i
);
258 courgette::SinkStream
* new_stream
= program_sinks
.stream(i
);
259 if (old_stream
== NULL
&& new_stream
== NULL
)
262 courgette::SourceStream old_source
;
263 courgette::SourceStream new_source
;
264 old_source
.Init(old_stream
? *old_stream
: empty_sink
);
265 new_source
.Init(new_stream
? *new_stream
: empty_sink
);
266 courgette::SinkStream patch_stream
;
267 courgette::BSDiffStatus status
=
268 courgette::CreateBinaryPatch(&old_source
, &new_source
, &patch_stream
);
269 if (status
!= courgette::OK
) Problem("-xxx failed.");
271 std::string append
= std::string("-") + base::IntToString(i
);
273 WriteSinkToFile(&patch_stream
,
274 output_file_root
.InsertBeforeExtensionASCII(append
));
278 void Assemble(const base::FilePath
& input_file
,
279 const base::FilePath
& output_file
) {
280 std::string buffer
= ReadOrFail(input_file
, "input");
282 courgette::SourceStreamSet sources
;
283 if (!sources
.Init(buffer
.c_str(), buffer
.length()))
284 Problem("Bad input file.");
286 courgette::EncodedProgram
* encoded
= NULL
;
287 const courgette::Status read_status
= ReadEncodedProgram(&sources
, &encoded
);
288 if (read_status
!= courgette::C_OK
)
289 Problem("Bad encoded program.");
291 courgette::SinkStream sink
;
293 const courgette::Status assemble_status
= courgette::Assemble(encoded
, &sink
);
294 if (assemble_status
!= courgette::C_OK
)
295 Problem("Can't assemble.");
297 WriteSinkToFile(&sink
, output_file
);
300 void GenerateEnsemblePatch(const base::FilePath
& old_file
,
301 const base::FilePath
& new_file
,
302 const base::FilePath
& patch_file
) {
303 std::string old_buffer
= ReadOrFail(old_file
, "'old' input");
304 std::string new_buffer
= ReadOrFail(new_file
, "'new' input");
306 courgette::SourceStream old_stream
;
307 courgette::SourceStream new_stream
;
308 old_stream
.Init(old_buffer
);
309 new_stream
.Init(new_buffer
);
311 courgette::SinkStream patch_stream
;
312 courgette::Status status
=
313 courgette::GenerateEnsemblePatch(&old_stream
, &new_stream
, &patch_stream
);
315 if (status
!= courgette::C_OK
) Problem("-gen failed.");
317 WriteSinkToFile(&patch_stream
, patch_file
);
320 void ApplyEnsemblePatch(const base::FilePath
& old_file
,
321 const base::FilePath
& patch_file
,
322 const base::FilePath
& new_file
) {
323 // We do things a little differently here in order to call the same Courgette
324 // entry point as the installer. That entry point point takes file names and
325 // returns an status code but does not output any diagnostics.
327 courgette::Status status
=
328 courgette::ApplyEnsemblePatch(old_file
.value().c_str(),
329 patch_file
.value().c_str(),
330 new_file
.value().c_str());
332 if (status
== courgette::C_OK
)
335 // Diagnose the error.
337 case courgette::C_BAD_ENSEMBLE_MAGIC
:
338 Problem("Not a courgette patch");
341 case courgette::C_BAD_ENSEMBLE_VERSION
:
342 Problem("Wrong version patch");
345 case courgette::C_BAD_ENSEMBLE_HEADER
:
346 Problem("Corrupt patch");
349 case courgette::C_DISASSEMBLY_FAILED
:
350 Problem("Disassembly failed (could be because of memory issues)");
353 case courgette::C_STREAM_ERROR
:
354 Problem("Stream error (likely out of memory or disk space)");
361 // If we failed due to a missing input file, this will
362 // print the message.
363 std::string old_buffer
= ReadOrFail(old_file
, "'old' input");
365 std::string patch_buffer
= ReadOrFail(patch_file
, "'patch' input");
366 patch_buffer
.clear();
368 // Non-input related errors:
369 if (status
== courgette::C_WRITE_OPEN_ERROR
)
370 Problem("Can't open output");
371 if (status
== courgette::C_WRITE_ERROR
)
372 Problem("Can't write output");
374 Problem("-apply failed.");
377 void GenerateBSDiffPatch(const base::FilePath
& old_file
,
378 const base::FilePath
& new_file
,
379 const base::FilePath
& patch_file
) {
380 std::string old_buffer
= ReadOrFail(old_file
, "'old' input");
381 std::string new_buffer
= ReadOrFail(new_file
, "'new' input");
383 courgette::SourceStream old_stream
;
384 courgette::SourceStream new_stream
;
385 old_stream
.Init(old_buffer
);
386 new_stream
.Init(new_buffer
);
388 courgette::SinkStream patch_stream
;
389 courgette::BSDiffStatus status
=
390 courgette::CreateBinaryPatch(&old_stream
, &new_stream
, &patch_stream
);
392 if (status
!= courgette::OK
) Problem("-genbsdiff failed.");
394 WriteSinkToFile(&patch_stream
, patch_file
);
397 void ApplyBSDiffPatch(const base::FilePath
& old_file
,
398 const base::FilePath
& patch_file
,
399 const base::FilePath
& new_file
) {
400 std::string old_buffer
= ReadOrFail(old_file
, "'old' input");
401 std::string patch_buffer
= ReadOrFail(patch_file
, "'patch' input");
403 courgette::SourceStream old_stream
;
404 courgette::SourceStream patch_stream
;
405 old_stream
.Init(old_buffer
);
406 patch_stream
.Init(patch_buffer
);
408 courgette::SinkStream new_stream
;
409 courgette::BSDiffStatus status
=
410 courgette::ApplyBinaryPatch(&old_stream
, &patch_stream
, &new_stream
);
412 if (status
!= courgette::OK
) Problem("-applybsdiff failed.");
414 WriteSinkToFile(&new_stream
, new_file
);
417 int main(int argc
, const char* argv
[]) {
418 base::AtExitManager at_exit_manager
;
419 CommandLine::Init(argc
, argv
);
420 const CommandLine
& command_line
= *CommandLine::ForCurrentProcess();
422 (void)logging::InitLogging(
423 FILE_PATH_LITERAL("courgette.log"),
424 logging::LOG_TO_BOTH_FILE_AND_SYSTEM_DEBUG_LOG
,
425 logging::LOCK_LOG_FILE
,
426 logging::APPEND_TO_OLD_LOG_FILE
,
427 logging::DISABLE_DCHECK_FOR_NON_OFFICIAL_RELEASE_BUILDS
);
428 logging::SetMinLogLevel(logging::LOG_VERBOSE
);
430 bool cmd_sup
= command_line
.HasSwitch("supported");
431 bool cmd_dis
= command_line
.HasSwitch("dis");
432 bool cmd_asm
= command_line
.HasSwitch("asm");
433 bool cmd_disadj
= command_line
.HasSwitch("disadj");
434 bool cmd_make_patch
= command_line
.HasSwitch("gen");
435 bool cmd_apply_patch
= command_line
.HasSwitch("apply");
436 bool cmd_make_bsdiff_patch
= command_line
.HasSwitch("genbsdiff");
437 bool cmd_apply_bsdiff_patch
= command_line
.HasSwitch("applybsdiff");
438 bool cmd_spread_1_adjusted
= command_line
.HasSwitch("gen1a");
439 bool cmd_spread_1_unadjusted
= command_line
.HasSwitch("gen1u");
441 std::vector
<base::FilePath
> values
;
442 const CommandLine::StringVector
& args
= command_line
.GetArgs();
443 for (size_t i
= 0; i
< args
.size(); ++i
) {
444 values
.push_back(base::FilePath(args
[i
]));
447 // '-repeat=N' is for debugging. Running many iterations can reveal leaks and
449 int repeat_count
= 1;
450 std::string repeat_switch
= command_line
.GetSwitchValueASCII("repeat");
451 if (!repeat_switch
.empty())
452 if (!base::StringToInt(repeat_switch
, &repeat_count
))
455 if (cmd_sup
+ cmd_dis
+ cmd_asm
+ cmd_disadj
+ cmd_make_patch
+
456 cmd_apply_patch
+ cmd_make_bsdiff_patch
+ cmd_apply_bsdiff_patch
+
457 cmd_spread_1_adjusted
+ cmd_spread_1_unadjusted
460 "Must have exactly one of:\n"
461 " -supported -asm, -dis, -disadj, -gen or -apply, -genbsdiff"
462 " or -applybsdiff.");
464 while (repeat_count
-- > 0) {
466 if (values
.size() != 1)
467 UsageProblem("-supported <executable_file>");
468 return !Supported(values
[0]);
469 } else if (cmd_dis
) {
470 if (values
.size() != 2)
471 UsageProblem("-dis <executable_file> <courgette_file>");
472 Disassemble(values
[0], values
[1]);
473 } else if (cmd_asm
) {
474 if (values
.size() != 2)
475 UsageProblem("-asm <courgette_file_input> <executable_file_output>");
476 Assemble(values
[0], values
[1]);
477 } else if (cmd_disadj
) {
478 if (values
.size() != 3)
479 UsageProblem("-disadj <executable_file> <model> <courgette_file>");
480 DisassembleAndAdjust(values
[0], values
[1], values
[2]);
481 } else if (cmd_make_patch
) {
482 if (values
.size() != 3)
483 UsageProblem("-gen <old_file> <new_file> <patch_file>");
484 GenerateEnsemblePatch(values
[0], values
[1], values
[2]);
485 } else if (cmd_apply_patch
) {
486 if (values
.size() != 3)
487 UsageProblem("-apply <old_file> <patch_file> <new_file>");
488 ApplyEnsemblePatch(values
[0], values
[1], values
[2]);
489 } else if (cmd_make_bsdiff_patch
) {
490 if (values
.size() != 3)
491 UsageProblem("-genbsdiff <old_file> <new_file> <patch_file>");
492 GenerateBSDiffPatch(values
[0], values
[1], values
[2]);
493 } else if (cmd_apply_bsdiff_patch
) {
494 if (values
.size() != 3)
495 UsageProblem("-applybsdiff <old_file> <patch_file> <new_file>");
496 ApplyBSDiffPatch(values
[0], values
[1], values
[2]);
497 } else if (cmd_spread_1_adjusted
|| cmd_spread_1_unadjusted
) {
498 if (values
.size() != 3)
499 UsageProblem("-gen1[au] <old_file> <new_file> <patch_files_root>");
500 DisassembleAdjustDiff(values
[0], values
[1], values
[2],
501 cmd_spread_1_adjusted
);
503 UsageProblem("No operation specified");