2017-01-11 15:26:58 +01:00
|
|
|
/*
|
|
|
|
* cbfs-compression-tool, CLI utility for dealing with CBFS compressed data
|
|
|
|
*
|
|
|
|
* Copyright (C) 2017 Google, Inc.
|
|
|
|
*
|
|
|
|
* This program is free software; you can redistribute it and/or modify
|
|
|
|
* it under the terms of the GNU General Public License as published by
|
|
|
|
* the Free Software Foundation; version 2 of the License.
|
|
|
|
*
|
|
|
|
* This program is distributed in the hope that it will be useful,
|
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
* GNU General Public License for more details.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include <stdio.h>
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include <string.h>
|
|
|
|
#include <time.h>
|
|
|
|
|
|
|
|
#include "common.h"
|
|
|
|
|
|
|
|
const char *usage_text = "cbfs-compression-tool benchmark\n"
|
|
|
|
" runs benchmarks for all implemented algorithms\n"
|
|
|
|
"cbfs-compression-tool compress inFile outFile algo\n"
|
|
|
|
" compresses inFile with algo and stores in outFile\n"
|
|
|
|
"\n"
|
|
|
|
"'compress' file format:\n"
|
|
|
|
" 4 bytes little endian: algorithm ID (as used in CBFS)\n"
|
|
|
|
" 4 bytes little endian: uncompressed size\n"
|
|
|
|
" ...: compressed data stream\n";
|
|
|
|
|
2018-05-16 02:30:20 +02:00
|
|
|
static void usage(void)
|
2017-01-11 15:26:58 +01:00
|
|
|
{
|
|
|
|
puts(usage_text);
|
|
|
|
}
|
|
|
|
|
2018-05-16 02:30:20 +02:00
|
|
|
static int benchmark(void)
|
2017-01-11 15:26:58 +01:00
|
|
|
{
|
|
|
|
const int bufsize = 10*1024*1024;
|
|
|
|
char *data = malloc(bufsize);
|
|
|
|
if (!data) {
|
|
|
|
fprintf(stderr, "out of memory\n");
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
char *compressed_data = malloc(bufsize);
|
|
|
|
if (!compressed_data) {
|
2017-01-13 13:30:54 +01:00
|
|
|
free(data);
|
2017-01-11 15:26:58 +01:00
|
|
|
fprintf(stderr, "out of memory\n");
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
int i, l = strlen(usage_text) + 1;
|
|
|
|
for (i = 0; i + l < bufsize; i += l) {
|
|
|
|
memcpy(data + i, usage_text, l);
|
|
|
|
}
|
|
|
|
memset(data + i, 0, bufsize - i);
|
|
|
|
const struct typedesc_t *algo;
|
|
|
|
for (algo = &types_cbfs_compression[0]; algo->name != NULL; algo++) {
|
|
|
|
int outsize = bufsize;
|
|
|
|
printf("measuring '%s'\n", algo->name);
|
|
|
|
comp_func_ptr comp = compression_function(algo->type);
|
|
|
|
if (comp == NULL) {
|
|
|
|
printf("no handler associated with algorithm\n");
|
2017-01-13 13:30:54 +01:00
|
|
|
free(data);
|
|
|
|
free(compressed_data);
|
2017-01-11 15:26:58 +01:00
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
struct timespec t_s, t_e;
|
|
|
|
clock_gettime(CLOCK_MONOTONIC, &t_s);
|
|
|
|
|
|
|
|
if (comp(data, bufsize, compressed_data, &outsize)) {
|
|
|
|
printf("compression failed");
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
clock_gettime(CLOCK_MONOTONIC, &t_e);
|
|
|
|
printf("compressing %d bytes to %d took %ld seconds\n",
|
|
|
|
bufsize, outsize,
|
2017-05-25 04:28:57 +02:00
|
|
|
(long)(t_e.tv_sec - t_s.tv_sec));
|
2017-01-11 15:26:58 +01:00
|
|
|
}
|
2017-01-13 13:30:54 +01:00
|
|
|
free(data);
|
|
|
|
free(compressed_data);
|
2017-01-11 15:26:58 +01:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2018-05-16 02:30:20 +02:00
|
|
|
static int compress(char *infile, char *outfile, char *algoname,
|
|
|
|
int write_header)
|
2017-01-11 15:26:58 +01:00
|
|
|
{
|
|
|
|
int err = 1;
|
|
|
|
FILE *fin = NULL;
|
|
|
|
FILE *fout = NULL;
|
|
|
|
void *indata = NULL;
|
|
|
|
|
|
|
|
const struct typedesc_t *algo = &types_cbfs_compression[0];
|
|
|
|
while (algo->name != NULL) {
|
cbfs-compression-tool: Fix minor edge cases in algorithm type parsing
This patch adds two minor improvements to the way cbfs-compression-tool
parses the compression algorithm type that is passed through the -t
option of the 'compress' subcommand. These improvements are intended
to prevent accidents and unexpected behavior when using the
cbfs-compression-tool, in particular in automated contexts such as a
Makefile rule.
In the first part of this patch, a return statement is inserted after
the 'if (algo->name == NULL)' check of the compress() function. This
causes the function to exit immediately and subsequently abort the
program when the algorithm type was not detected correctly. Previously,
execution would continue with the 'algo' pointer pointing to the zeroed
out stopper entry of the types_cbfs_compression[] array. The ultimate
effect of this would be to pass 0 as 'algo->type' to the
compression_function() function, which happens to be the same
enumeration value as is used for CBFS_COMPRESS_NONE, leading to a valid
compression function result that matches the behavior of no compression.
Thus, if a script calling cbfs-compression-tool compress contained a
typo in the -t parameter, it would continue running with an unintended
compression result rather than immediately exiting cleanly.
In the second part of this patch, the strcmp() function is replaced with
strcasecmp() when comparing 'algo->name' with the 'algoname' parameter
that was passed to the compress() function. strcasecmp() uses an
identical function signature as strcmp() and is thus suitable as a
drop-in replacement, but it differs in behavior: rather than only
returning a result of 0 when the two NULL-terminated input strings are
character by character identical, the strcasecmp() function applies a
weaker concept of identity where characters of the latin alphabet
(hexadecimal ranges 0x41 through 0x5a and 0x61 through 0x7a) are also
considered identical to other characters that differ from them only in
their case. This causes the -t parameter of cbfs-compression-tool
compress to also accept lowercase spellings of the available compression
algorithms, such as "lz4" instead of "LZ4" and "lzma" instead of "LZMA".
As an unintended but harmless side-effect, mixed-case spellings such as
"lZ4" or "LZmA" will also be recognized as valid compression algorithms.
(Note that since the character "4" (hexadecimal 0x34) of the "LZ4"
compression type name is not part of the above-mentioned ranges of latin
alphabet characters, no new substitutions become valid for that part of
the "LZ4" string with this patch.)
Change-Id: I375dbaeefaa0d4b0c5be81bf7668f8f330f1cf61
Signed-off-by: Julius Werner <jwerner@chromium.org>
Reviewed-on: https://review.coreboot.org/26389
Tested-by: build bot (Jenkins) <no-reply@coreboot.org>
Reviewed-by: Aaron Durbin <adurbin@chromium.org>
Reviewed-by: Paul Menzel <paulepanter@users.sourceforge.net>
2018-05-19 02:56:23 +02:00
|
|
|
if (strcasecmp(algo->name, algoname) == 0) break;
|
2017-01-11 15:26:58 +01:00
|
|
|
algo++;
|
|
|
|
}
|
|
|
|
if (algo->name == NULL) {
|
|
|
|
fprintf(stderr, "algo '%s' is not supported.\n", algoname);
|
cbfs-compression-tool: Fix minor edge cases in algorithm type parsing
This patch adds two minor improvements to the way cbfs-compression-tool
parses the compression algorithm type that is passed through the -t
option of the 'compress' subcommand. These improvements are intended
to prevent accidents and unexpected behavior when using the
cbfs-compression-tool, in particular in automated contexts such as a
Makefile rule.
In the first part of this patch, a return statement is inserted after
the 'if (algo->name == NULL)' check of the compress() function. This
causes the function to exit immediately and subsequently abort the
program when the algorithm type was not detected correctly. Previously,
execution would continue with the 'algo' pointer pointing to the zeroed
out stopper entry of the types_cbfs_compression[] array. The ultimate
effect of this would be to pass 0 as 'algo->type' to the
compression_function() function, which happens to be the same
enumeration value as is used for CBFS_COMPRESS_NONE, leading to a valid
compression function result that matches the behavior of no compression.
Thus, if a script calling cbfs-compression-tool compress contained a
typo in the -t parameter, it would continue running with an unintended
compression result rather than immediately exiting cleanly.
In the second part of this patch, the strcmp() function is replaced with
strcasecmp() when comparing 'algo->name' with the 'algoname' parameter
that was passed to the compress() function. strcasecmp() uses an
identical function signature as strcmp() and is thus suitable as a
drop-in replacement, but it differs in behavior: rather than only
returning a result of 0 when the two NULL-terminated input strings are
character by character identical, the strcasecmp() function applies a
weaker concept of identity where characters of the latin alphabet
(hexadecimal ranges 0x41 through 0x5a and 0x61 through 0x7a) are also
considered identical to other characters that differ from them only in
their case. This causes the -t parameter of cbfs-compression-tool
compress to also accept lowercase spellings of the available compression
algorithms, such as "lz4" instead of "LZ4" and "lzma" instead of "LZMA".
As an unintended but harmless side-effect, mixed-case spellings such as
"lZ4" or "LZmA" will also be recognized as valid compression algorithms.
(Note that since the character "4" (hexadecimal 0x34) of the "LZ4"
compression type name is not part of the above-mentioned ranges of latin
alphabet characters, no new substitutions become valid for that part of
the "LZ4" string with this patch.)
Change-Id: I375dbaeefaa0d4b0c5be81bf7668f8f330f1cf61
Signed-off-by: Julius Werner <jwerner@chromium.org>
Reviewed-on: https://review.coreboot.org/26389
Tested-by: build bot (Jenkins) <no-reply@coreboot.org>
Reviewed-by: Aaron Durbin <adurbin@chromium.org>
Reviewed-by: Paul Menzel <paulepanter@users.sourceforge.net>
2018-05-19 02:56:23 +02:00
|
|
|
return 1;
|
2017-01-11 15:26:58 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
comp_func_ptr comp = compression_function(algo->type);
|
|
|
|
if (comp == NULL) {
|
|
|
|
printf("no handler associated with algorithm\n");
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
fin = fopen(infile, "rb");
|
|
|
|
if (!fin) {
|
|
|
|
fprintf(stderr, "could not open '%s'\n", infile);
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
fout = fopen(outfile, "wb");
|
|
|
|
if (!fout) {
|
|
|
|
fprintf(stderr, "could not open '%s' for writing\n", outfile);
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (fseek(fin, 0, SEEK_END) != 0) {
|
|
|
|
fprintf(stderr, "could not seek in input\n");
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
long insize = ftell(fin);
|
|
|
|
if (insize < 0) {
|
|
|
|
fprintf(stderr, "could not determine input size\n");
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
rewind(fin);
|
|
|
|
|
|
|
|
indata = malloc(insize);
|
|
|
|
if (!indata) {
|
|
|
|
fprintf(stderr, "out of memory\n");
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
void *outdata = malloc(insize);
|
|
|
|
if (!outdata) {
|
|
|
|
fprintf(stderr, "out of memory\n");
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
int outsize;
|
|
|
|
|
|
|
|
int remsize = insize;
|
|
|
|
while (remsize > 0) {
|
|
|
|
int readsz = fread(indata, 1, remsize, fin);
|
|
|
|
if (readsz < 0) {
|
|
|
|
fprintf(stderr, "failed to read input with %d bytes left\n", remsize);
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
remsize -= readsz;
|
|
|
|
}
|
|
|
|
|
2017-01-23 09:35:44 +01:00
|
|
|
if (comp(indata, insize, outdata, &outsize) == -1) {
|
|
|
|
outsize = insize;
|
|
|
|
free(outdata);
|
|
|
|
outdata = indata;
|
|
|
|
algo = &types_cbfs_compression[0];
|
|
|
|
}
|
2017-01-11 15:26:58 +01:00
|
|
|
|
2018-05-16 02:30:20 +02:00
|
|
|
if (write_header) {
|
|
|
|
char header[8];
|
|
|
|
header[0] = algo->type & 0xff;
|
|
|
|
header[1] = (algo->type >> 8) & 0xff;
|
|
|
|
header[2] = (algo->type >> 16) & 0xff;
|
|
|
|
header[3] = (algo->type >> 24) & 0xff;
|
|
|
|
header[4] = insize & 0xff;
|
|
|
|
header[5] = (insize >> 8) & 0xff;
|
|
|
|
header[6] = (insize >> 16) & 0xff;
|
|
|
|
header[7] = (insize >> 24) & 0xff;
|
|
|
|
if (fwrite(header, 8, 1, fout) != 1) {
|
|
|
|
fprintf(stderr, "failed writing header\n");
|
|
|
|
goto out;
|
|
|
|
}
|
2017-01-11 15:26:58 +01:00
|
|
|
}
|
|
|
|
if (fwrite(outdata, outsize, 1, fout) != 1) {
|
|
|
|
fprintf(stderr, "failed writing compressed data\n");
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
err = 0;
|
|
|
|
out:
|
|
|
|
if (fin) fclose(fin);
|
|
|
|
if (fout) fclose(fout);
|
|
|
|
if (indata) free(indata);
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
|
|
|
int main(int argc, char **argv)
|
|
|
|
{
|
|
|
|
if ((argc == 2) && (strcmp(argv[1], "benchmark") == 0))
|
|
|
|
return benchmark();
|
|
|
|
if ((argc == 5) && (strcmp(argv[1], "compress") == 0))
|
2018-05-16 02:30:20 +02:00
|
|
|
return compress(argv[2], argv[3], argv[4], 1);
|
|
|
|
if ((argc == 5) && (strcmp(argv[1], "rawcompress") == 0))
|
|
|
|
return compress(argv[2], argv[3], argv[4], 0);
|
2017-01-11 15:26:58 +01:00
|
|
|
usage();
|
|
|
|
return 1;
|
|
|
|
}
|