programs/gzip: add support for the '-t' option (test file integrity)

The '-t' option of GNU gzip allows checking whether a gzip file is valid
without writing the data anywhere.  It's relatively straightforward to
support in libdeflate-gzip too, so add support for it.

Resolves https://github.com/ebiggers/libdeflate/issues/125

[EB - updated commit message]
This commit is contained in:
tansy 2021-04-07 16:19:00 +02:00 committed by Eric Biggers
parent fbada10aa9
commit 72c81b3332
2 changed files with 43 additions and 6 deletions

View File

@ -43,11 +43,12 @@ struct options {
bool decompress; bool decompress;
bool force; bool force;
bool keep; bool keep;
bool test;
int compression_level; int compression_level;
const tchar *suffix; const tchar *suffix;
}; };
static const tchar *const optstring = T("1::2::3::4::5::6::7::8::9::cdfhknS:V"); static const tchar *const optstring = T("1::2::3::4::5::6::7::8::9::cdfhknS:tV");
static void static void
show_usage(FILE *fp) show_usage(FILE *fp)
@ -66,6 +67,7 @@ show_usage(FILE *fp)
" -h print this help\n" " -h print this help\n"
" -k don't delete input files\n" " -k don't delete input files\n"
" -S SUF use suffix SUF instead of .gz\n" " -S SUF use suffix SUF instead of .gz\n"
" -t test file integrity\n"
" -V show version and legal information\n", " -V show version and legal information\n",
prog_invocation_name); prog_invocation_name);
} }
@ -183,7 +185,8 @@ load_u32_gzip(const u8 *p)
static int static int
do_decompress(struct libdeflate_decompressor *decompressor, do_decompress(struct libdeflate_decompressor *decompressor,
struct file_stream *in, struct file_stream *out) struct file_stream *in, struct file_stream *out,
const struct options *options)
{ {
const u8 *compressed_data = in->mmap_mem; const u8 *compressed_data = in->mmap_mem;
size_t compressed_size = in->mmap_size; size_t compressed_size = in->mmap_size;
@ -258,9 +261,11 @@ do_decompress(struct libdeflate_decompressor *decompressor,
goto out; goto out;
} }
ret = full_write(out, uncompressed_data, actual_out_nbytes); if (!options->test) {
if (ret != 0) ret = full_write(out, uncompressed_data, actual_out_nbytes);
goto out; if (ret != 0)
goto out;
}
compressed_data += actual_in_nbytes; compressed_data += actual_in_nbytes;
compressed_size -= actual_in_nbytes; compressed_size -= actual_in_nbytes;
@ -425,7 +430,7 @@ decompress_file(struct libdeflate_decompressor *decompressor, const tchar *path,
if (ret != 0) if (ret != 0)
goto out_close_out; goto out_close_out;
ret = do_decompress(decompressor, &in, &out); ret = do_decompress(decompressor, &in, &out, options);
if (ret != 0) if (ret != 0)
goto out_close_out; goto out_close_out;
@ -534,6 +539,7 @@ tmain(int argc, tchar *argv[])
options.decompress = is_gunzip(); options.decompress = is_gunzip();
options.force = false; options.force = false;
options.keep = false; options.keep = false;
options.test = false;
options.compression_level = 6; options.compression_level = 6;
options.suffix = T(".gz"); options.suffix = T(".gz");
@ -583,6 +589,17 @@ tmain(int argc, tchar *argv[])
return 1; return 1;
} }
break; break;
case 't':
options.test = true;
options.decompress = true;
options.to_stdout = true;
/*
* -t behaves just like the more commonly used -c
* option, except that -t doesn't actually write
* anything. For ease of implementation, just pretend
* that -c was specified too.
*/
break;
case 'V': case 'V':
show_version(); show_version();
return 0; return 0;

View File

@ -463,6 +463,26 @@ for prog in gzip gunzip; do
done done
begin_test '-t (test) option works'
good_files=(
'H4sIAAAAAAAAA3PMSVTITVTIzi9JVABTIJ5jzpGZelwAX+86ehsAAAA='
'H4sIAAAAAAAAAwvJSFUoLM1MzlZIKsovz1NIy69QyCrNLShWyC9LLVIoAUrnJFZVKqTkp+txAQBqzFDrLQAAAA==')
bad_files=(
'H4sIAO1YYmAAA3PMSVTITVTIzi9JVABTIJ5jzpGZelwAX+46ehsAAAA='
'H4sIAO1YYmAAA3PMSVTITVTIzi85VABTIJ5jzpGZelwAX+86ehsAAAA='
'H4sIAAAAAAAAA3PMSVTITVTIzi9JVABTIJ5jzpGZelwAX+86ehsBAAA='
'H4sIAAAAAAAAAwvJSFUoLM1MzlZIKsovz1NIy69QyCrNLShWyC9LLVIogUrnJFZVKqTkp+txAQBqzFDrLQAAAA=='
'H4sIAAAAAAAAAwvJSFUoLM1MzlZIKsovz1NIy69QyCrNLShWyC9L')
for contents in "${good_files[@]}"; do
echo "$contents" | base64 -d | gzip -t
done
for contents in "${bad_files[@]}"; do
echo "$contents" | base64 -d > file
assert_error '\<invalid compressed data|file corrupt|unexpected end of file|Out of memory\>' \
gzip -t file
done
begin_test 'Version information' begin_test 'Version information'
gzip -V | grep -q Copyright gzip -V | grep -q Copyright
gunzip -V | grep -q Copyright gunzip -V | grep -q Copyright