You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
149 lines
4.7 KiB
149 lines
4.7 KiB
// Copyright 2015 The Chromium OS 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 <err.h>
|
|
#include <fcntl.h>
|
|
#include <getopt.h>
|
|
#include <sys/mman.h>
|
|
#include <sys/stat.h>
|
|
#include <sys/types.h>
|
|
#include <unistd.h>
|
|
|
|
#include <stddef.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
|
|
#include <iostream>
|
|
#include <limits>
|
|
|
|
#include "bsdiff/bsdiff.h"
|
|
#include "bsdiff/bsdiff_arguments.h"
|
|
#include "bsdiff/constants.h"
|
|
#include "bsdiff/patch_writer_factory.h"
|
|
|
|
namespace {
|
|
|
|
// mmap() the passed |filename| to read-only memory and store in |filesize| the
|
|
// size of the file. To release the memory, call munmap with the returned
|
|
// pointer and filesize. In case of error returns nullptr.
|
|
void* MapFile(const char* filename, size_t* filesize) {
|
|
int fd = open(filename, O_RDONLY);
|
|
if (fd < 0) {
|
|
perror("open()");
|
|
return nullptr;
|
|
}
|
|
|
|
struct stat st;
|
|
fstat(fd, &st);
|
|
if (static_cast<uint64_t>(st.st_size) > std::numeric_limits<size_t>::max()) {
|
|
fprintf(stderr, "File too big\n");
|
|
close(fd);
|
|
return nullptr;
|
|
}
|
|
*filesize = st.st_size;
|
|
|
|
void* ret = mmap(nullptr, st.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
|
|
if (ret == MAP_FAILED) {
|
|
perror("mmap()");
|
|
close(fd);
|
|
return nullptr;
|
|
}
|
|
close(fd);
|
|
return ret;
|
|
}
|
|
|
|
// Generate bsdiff patch from the |old_filename| file to the |new_filename|
|
|
// file with options in |arguments|. Store the resulting patch in a new
|
|
// |patch_filename| file. Returns 0 on success.
|
|
int GenerateBsdiffFromFiles(const char* old_filename,
|
|
const char* new_filename,
|
|
const char* patch_filename,
|
|
const bsdiff::BsdiffArguments& arguments) {
|
|
size_t oldsize;
|
|
uint8_t* old_buf = static_cast<uint8_t*>(MapFile(old_filename, &oldsize));
|
|
if (!old_buf) {
|
|
return 1;
|
|
}
|
|
|
|
size_t newsize;
|
|
uint8_t* new_buf = static_cast<uint8_t*>(MapFile(new_filename, &newsize));
|
|
if (!new_buf) {
|
|
munmap(old_buf, oldsize);
|
|
return 1;
|
|
}
|
|
|
|
std::unique_ptr<bsdiff::PatchWriterInterface> patch_writer;
|
|
std::vector<uint8_t> raw_data;
|
|
|
|
if (arguments.format() == bsdiff::BsdiffFormat::kLegacy) {
|
|
patch_writer = bsdiff::CreateBsdiffPatchWriter(patch_filename);
|
|
} else if (arguments.format() == bsdiff::BsdiffFormat::kBsdf2) {
|
|
patch_writer = bsdiff::CreateBSDF2PatchWriter(patch_filename,
|
|
arguments.compressor_types(),
|
|
arguments.brotli_quality());
|
|
} else if (arguments.format() == bsdiff::BsdiffFormat::kEndsley) {
|
|
patch_writer = bsdiff::CreateEndsleyPatchWriter(
|
|
&raw_data, arguments.compressor_types()[0], arguments.brotli_quality());
|
|
} else {
|
|
std::cerr << "unexpected bsdiff format." << std::endl;
|
|
return 1;
|
|
}
|
|
|
|
int ret = bsdiff::bsdiff(old_buf, oldsize, new_buf, newsize,
|
|
arguments.min_length(), patch_writer.get(), nullptr);
|
|
|
|
munmap(old_buf, oldsize);
|
|
munmap(new_buf, newsize);
|
|
|
|
if (!ret && arguments.format() == bsdiff::BsdiffFormat::kEndsley) {
|
|
// Store the raw_data on disk.
|
|
FILE* fp = fopen(patch_filename, "wb");
|
|
if (!fp) {
|
|
perror("Opening the patch file");
|
|
return 1;
|
|
}
|
|
if (raw_data.size() != fwrite(raw_data.data(), 1, raw_data.size(), fp)) {
|
|
perror("Writing to the patch file");
|
|
ret = 1;
|
|
}
|
|
fclose(fp);
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
void PrintUsage(const std::string& proc_name) {
|
|
std::cerr << "usage: " << proc_name
|
|
<< " [options] oldfile newfile patchfile\n";
|
|
std::cerr << " --format <legacy|bsdiff40|bsdf2|endsley> The format of the"
|
|
" bsdiff patch.\n"
|
|
<< " --minlen LEN The minimum match length "
|
|
"required to consider a match in the algorithm.\n"
|
|
<< " --type <bz2|brotli|nocompression> The algorithm to compress "
|
|
"the patch, bsdf2 format only. Multiple supported compressors "
|
|
"should be split by ':', e.g. bz2:brotli.\n"
|
|
<< " --brotli_quality Quality of the brotli "
|
|
"compressor.\n";
|
|
}
|
|
|
|
} // namespace
|
|
|
|
int main(int argc, char* argv[]) {
|
|
bsdiff::BsdiffArguments arguments;
|
|
|
|
if (!arguments.ParseCommandLine(argc, argv)) {
|
|
PrintUsage(argv[0]);
|
|
return 1;
|
|
}
|
|
|
|
// The optind will be updated in ParseCommandLine to parse the options; and
|
|
// we expect the rest of the arguments to be oldfile, newfile, patchfile.
|
|
if (!arguments.IsValid() || argc - optind != 3) {
|
|
PrintUsage(argv[0]);
|
|
return 1;
|
|
}
|
|
|
|
return GenerateBsdiffFromFiles(argv[optind], argv[optind + 1],
|
|
argv[optind + 2], arguments);
|
|
}
|