Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Fetching contributors…

Cannot retrieve contributors at this time

471 lines (406 sloc) 13.757 kB
/*
Copyright (C) 2006-2012 Con Kolivas
Copyright (C) 1998-2003 Andrew Tridgell
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; either version 2 of the License, or
(at your option) any later version.
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.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/* rzip decompression algorithm */
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
#include <sys/types.h>
#ifdef HAVE_SYS_STAT_H
# include <sys/stat.h>
#endif
#ifdef HAVE_SYS_TIME_H
# include <sys/time.h>
#endif
#ifdef HAVE_UNISTD_H
# include <unistd.h>
#endif
#ifdef HAVE_ENDIAN_H
# include <endian.h>
#elif HAVE_SYS_ENDIAN_H
# include <sys/endian.h>
#endif
#ifdef HAVE_ARPA_INET_H
# include <arpa/inet.h>
#endif
#include "md5.h"
#include "runzip.h"
#include "stream.h"
#include "util.h"
#include "lrzip_core.h"
/* needed for CRC routines */
#include "lzma/C/7zCrc.h"
static inline uchar read_u8(rzip_control *control, void *ss, int stream, bool *err)
{
uchar b;
if (unlikely(read_stream(control, ss, stream, &b, 1) != 1)) {
*err = true;
fatal_return(("Stream read u8 failed\n"), 0);
}
return b;
}
static inline u32 read_u32(rzip_control *control, void *ss, int stream, bool *err)
{
u32 ret;
if (unlikely(read_stream(control, ss, stream, (uchar *)&ret, 4) != 4)) {
*err = true;
fatal_return(("Stream read u32 failed\n"), 0);
}
ret = le32toh(ret);
return ret;
}
/* Read a variable length of chars dependant on how big the chunk was */
static inline i64 read_vchars(rzip_control *control, void *ss, int stream, int length)
{
i64 s = 0;
if (unlikely(read_stream(control, ss, stream, (uchar *)&s, length) != length))
fatal_return(("Stream read of %d bytes failed\n", length), -1);
s = le64toh(s);
return s;
}
static i64 seekcur_fdout(rzip_control *control)
{
if (!TMP_OUTBUF)
return lseek(control->fd_out, 0, SEEK_CUR);
return (control->out_relofs + control->out_ofs);
}
static i64 seekto_fdhist(rzip_control *control, i64 pos)
{
if (!TMP_OUTBUF)
return lseek(control->fd_hist, pos, SEEK_SET);
control->hist_ofs = pos - control->out_relofs;
if (control->hist_ofs > control->out_len)
control->out_len = control->hist_ofs;
if (unlikely(control->hist_ofs < 0 || control->hist_ofs > control->out_maxlen)) {
print_err("Trying to seek outside tmpoutbuf to %lld in seekto_fdhist\n", control->hist_ofs);
return -1;
}
return pos;
}
static i64 seekcur_fdin(rzip_control *control)
{
if (!TMP_INBUF)
return lseek(control->fd_in, 0, SEEK_CUR);
return control->in_ofs;
}
static i64 seekto_fdin(rzip_control *control, i64 pos)
{
if (!TMP_INBUF)
return lseek(control->fd_in, pos, SEEK_SET);
if (unlikely(pos > control->in_len || pos < 0)) {
print_err("Trying to seek outside tmpinbuf to %lld in seekto_fdin\n", pos);
return -1;
}
control->in_ofs = pos;
return 0;
}
static i64 seekto_fdinend(rzip_control *control)
{
int tmpchar;
if (!TMP_INBUF)
return lseek(control->fd_in, 0, SEEK_END);
while ((tmpchar = getchar()) != EOF) {
control->tmp_inbuf[control->in_len++] = (char)tmpchar;
if (unlikely(control->in_len > control->in_maxlen))
failure_return(("Trying to read greater than max_len\n"), -1);
}
control->in_ofs = control->in_len;
return control->in_ofs;
}
static i64 read_header(rzip_control *control, void *ss, uchar *head)
{
int chunk_bytes = 2;
bool err = false;
/* All chunks were unnecessarily encoded 8 bytes wide version 0.4x */
if (control->major_version == 0 && control->minor_version == 4)
chunk_bytes = 8;
*head = read_u8(control, ss, 0, &err);
if (err)
return -1;
return read_vchars(control, ss, 0, chunk_bytes);
}
static i64 unzip_literal(rzip_control *control, void *ss, i64 len, uint32 *cksum)
{
i64 stream_read;
uchar *buf;
if (unlikely(len < 0))
failure_return(("len %lld is negative in unzip_literal!\n",len), -1);
buf = (uchar *)malloc(len);
if (unlikely(!buf))
fatal_return(("Failed to malloc literal buffer of size %lld\n", len), -1);
stream_read = read_stream(control, ss, 1, buf, len);
if (unlikely(stream_read == -1 )) {
free(buf);
fatal_return(("Failed to read_stream in unzip_literal\n"), -1);
}
if (unlikely(write_1g(control, buf, (size_t)stream_read) != (ssize_t)stream_read)) {
free(buf);
fatal_return(("Failed to write literal buffer of size %lld\n", stream_read), -1);
}
if (!HAS_MD5)
*cksum = CrcUpdate(*cksum, buf, stream_read);
if (!NO_MD5)
md5_process_bytes(buf, stream_read, &control->ctx);
free(buf);
return stream_read;
}
static i64 read_fdhist(rzip_control *control, void *buf, i64 len)
{
if (!TMP_OUTBUF)
return read_1g(control, control->fd_hist, buf, len);
if (unlikely(len + control->hist_ofs > control->out_maxlen)) {
print_err("Trying to read beyond end of tmpoutbuf in read_fdhist\n");
return -1;
}
memcpy(buf, control->tmp_outbuf + control->hist_ofs, len);
return len;
}
static i64 unzip_match(rzip_control *control, void *ss, i64 len, uint32 *cksum, int chunk_bytes)
{
i64 offset, n, total, cur_pos;
uchar *buf, *off_buf;
if (unlikely(len < 0))
failure_return(("len %lld is negative in unzip_match!\n",len), -1);
total = 0;
cur_pos = seekcur_fdout(control);
if (unlikely(cur_pos == -1))
fatal_return(("Seek failed on out file in unzip_match.\n"), -1);
/* Note the offset is in a different format v0.40+ */
offset = read_vchars(control, ss, 0, chunk_bytes);
if (unlikely(offset == -1))
return -1;
if (unlikely(seekto_fdhist(control, cur_pos - offset) == -1))
fatal_return(("Seek failed by %d from %d on history file in unzip_match\n",
offset, cur_pos), -1);
buf = (uchar *)malloc(len);
if (unlikely(!buf))
fatal_return(("Failed to malloc match buffer of size %lld\n", len), -1);
off_buf = buf;
while (len) {
n = MIN(len, offset);
if (unlikely(read_fdhist(control, off_buf, (size_t)n) != (ssize_t)n)) {
free(buf);
fatal_return(("Failed to read %d bytes in unzip_match\n", n), -1);
}
if (unlikely(write_1g(control, off_buf, (size_t)n) != (ssize_t)n)) {
free(buf);
fatal_return(("Failed to write %d bytes in unzip_match\n", n), -1);
}
if (!HAS_MD5)
*cksum = CrcUpdate(*cksum, off_buf, n);
if (!NO_MD5)
md5_process_bytes(off_buf, n, &control->ctx);
len -= n;
off_buf += n;
total += n;
}
free(buf);
return total;
}
/* decompress a section of an open file. Call fatal_return(() on error
return the number of bytes that have been retrieved
*/
static i64 runzip_chunk(rzip_control *control, int fd_in, i64 expected_size, i64 tally)
{
uint32 good_cksum, cksum = 0;
i64 len, ofs, total = 0;
int l = -1, p = 0;
char chunk_bytes;
struct stat st;
uchar head;
void *ss;
bool err = false;
/* for display of progress */
unsigned long divisor[] = {1,1024,1048576,1073741824U};
char *suffix[] = {"","KB","MB","GB"};
double prog_done, prog_tsize;
int divisor_index;
if (expected_size > (i64)10737418240ULL) /* > 10GB */
divisor_index = 3;
else if (expected_size > 10485760) /* > 10MB */
divisor_index = 2;
else if (expected_size > 10240) /* > 10KB */
divisor_index = 1;
else
divisor_index = 0;
prog_tsize = (long double)expected_size / (long double)divisor[divisor_index];
/* Determine the chunk_byte width size. Versions < 0.4 used 4
* bytes for all offsets, version 0.4 used 8 bytes. Versions 0.5+ use
* a variable number of bytes depending on chunk size.*/
if (control->major_version == 0 && control->minor_version < 4)
chunk_bytes = 4;
else if (control->major_version == 0 && control->minor_version == 4)
chunk_bytes = 8;
else {
print_maxverbose("Reading chunk_bytes at %lld\n", get_readseek(control, fd_in));
/* Read in the stored chunk byte width from the file */
if (unlikely(read_1g(control, fd_in, &chunk_bytes, 1) != 1))
fatal_return(("Failed to read chunk_bytes size in runzip_chunk\n"), -1);
if (unlikely(chunk_bytes < 1 || chunk_bytes > 8))
failure_return(("chunk_bytes %d is invalid in runzip_chunk\n", chunk_bytes), -1);
}
if (!tally && expected_size)
print_maxverbose("Expected size: %lld\n", expected_size);
print_maxverbose("Chunk byte width: %d\n", chunk_bytes);
ofs = seekcur_fdin(control);
if (unlikely(ofs == -1))
fatal_return(("Failed to seek input file in runzip_fd\n"), -1);
if (fstat(fd_in, &st) || st.st_size - ofs == 0)
return 0;
ss = open_stream_in(control, fd_in, NUM_STREAMS, chunk_bytes);
if (unlikely(!ss))
failure_return(("Failed to open_stream_in in runzip_chunk\n"), -1);
while ((len = read_header(control, ss, &head)) || head) {
i64 u;
if (unlikely(len == -1))
return -1;
switch (head) {
case 0:
u = unzip_literal(control, ss, len, &cksum);
if (unlikely(u == -1)) {
close_stream_in(control, ss);
return -1;
}
total += u;
break;
default:
u = unzip_match(control, ss, len, &cksum, chunk_bytes);
if (unlikely(u == -1)) {
close_stream_in(control, ss);
return -1;
}
total += u;
break;
}
if (expected_size) {
p = 100 * ((double)(tally + total) / (double)expected_size);
if (p / 10 != l / 10) {
prog_done = (double)(tally + total) / (double)divisor[divisor_index];
print_progress("%3d%% %9.2f / %9.2f %s\r",
p, prog_done, prog_tsize, suffix[divisor_index] );
l = p;
}
}
}
if (!HAS_MD5) {
good_cksum = read_u32(control, ss, 0, &err);
if (unlikely(err)) {
close_stream_in(control, ss);
return -1;
}
if (unlikely(good_cksum != cksum)) {
close_stream_in(control, ss);
failure_return(("Bad checksum: 0x%08x - expected: 0x%08x\n", cksum, good_cksum), -1);
}
print_maxverbose("Checksum for block: 0x%08x\n", cksum);
}
if (unlikely(close_stream_in(control, ss)))
fatal("Failed to close stream!\n");
return total;
}
/* Decompress an open file. Call fatal_return(() on error
return the number of bytes that have been retrieved
*/
i64 runzip_fd(rzip_control *control, int fd_in, int fd_out, int fd_hist, i64 expected_size)
{
uchar md5_stored[MD5_DIGEST_SIZE];
struct timeval start,end;
i64 total = 0, u;
if (!NO_MD5)
md5_init_ctx (&control->ctx);
gettimeofday(&start,NULL);
do {
u = runzip_chunk(control, fd_in, expected_size, total);
if (unlikely(u == -1))
return -1;
total += u;
if (TMP_OUTBUF)
{ if (unlikely(!flush_tmpoutbuf(control)))
return -1; }
else if (STDOUT)
{ if (unlikely(!dump_tmpoutfile(control, fd_out)))
return -1; }
if (TMP_INBUF)
clear_tmpinbuf(control);
else if (STDIN)
if (unlikely(!clear_tmpinfile(control)))
return -1;
} while (total < expected_size || (!expected_size && !control->eof));
gettimeofday(&end,NULL);
if (!ENCRYPT)
print_progress("\nAverage DeCompression Speed: %6.3fMB/s\n",
(total / 1024 / 1024) / (double)((end.tv_sec-start.tv_sec)? : 1));
if (!NO_MD5) {
int i,j;
md5_finish_ctx (&control->ctx, control->md5_resblock);
if (HAS_MD5) {
i64 fdinend = seekto_fdinend(control);
if (unlikely(fdinend == -1))
failure_return(("Failed to seekto_fdinend in rzip_fd\n"), -1);
if (unlikely(seekto_fdin(control, fdinend - MD5_DIGEST_SIZE) == -1))
failure_return(("Failed to seekto_fdin in rzip_fd\n"), -1);
if (unlikely(read_1g(control, fd_in, md5_stored, MD5_DIGEST_SIZE) != MD5_DIGEST_SIZE))
fatal_return(("Failed to read md5 data in runzip_fd\n"), -1);
if (ENCRYPT)
if (unlikely(!lrz_decrypt(control, md5_stored, MD5_DIGEST_SIZE, control->salt_pass)))
return -1;
for (i = 0; i < MD5_DIGEST_SIZE; i++)
if (md5_stored[i] != control->md5_resblock[i]) {
print_output("MD5 CHECK FAILED.\nStored:");
for (j = 0; j < MD5_DIGEST_SIZE; j++)
print_output("%02x", md5_stored[j] & 0xFF);
print_output("\nOutput file:");
for (j = 0; j < MD5_DIGEST_SIZE; j++)
print_output("%02x", control->md5_resblock[j] & 0xFF);
failure_return(("\n"), -1);
}
}
if (HASH_CHECK || MAX_VERBOSE) {
print_output("MD5: ");
for (i = 0; i < MD5_DIGEST_SIZE; i++)
print_output("%02x", control->md5_resblock[i] & 0xFF);
print_output("\n");
}
if (CHECK_FILE) {
FILE *md5_fstream;
int i, j;
if (TMP_OUTBUF)
close_tmpoutbuf(control);
memcpy(md5_stored, control->md5_resblock, MD5_DIGEST_SIZE);
if (unlikely(seekto_fdhist(control, 0) == -1))
fatal_return(("Failed to seekto_fdhist in runzip_fd\n"), -1);
if (unlikely((md5_fstream = fdopen(fd_hist, "r")) == NULL))
fatal_return(("Failed to fdopen fd_hist in runzip_fd\n"), -1);
if (unlikely(md5_stream(md5_fstream, control->md5_resblock)))
fatal_return(("Failed to md5_stream in runzip_fd\n"), -1);
/* We don't close the file here as it's closed in main */
for (i = 0; i < MD5_DIGEST_SIZE; i++)
if (md5_stored[i] != control->md5_resblock[i]) {
print_output("MD5 CHECK FAILED.\nStored:");
for (j = 0; j < MD5_DIGEST_SIZE; j++)
print_output("%02x", md5_stored[j] & 0xFF);
print_output("\nOutput file:");
for (j = 0; j < MD5_DIGEST_SIZE; j++)
print_output("%02x", control->md5_resblock[j] & 0xFF);
failure_return(("\n"), -1);
}
print_output("MD5 integrity of written file matches archive\n");
if (!HAS_MD5)
print_output("Note this lrzip archive did not have a stored md5 value.\n"
"The archive decompression was validated with crc32 and the md5 hash was "
"calculated on decompression\n");
}
}
return total;
}
Jump to Line
Something went wrong with that request. Please try again.