2010-03-29 01:07:08 +02:00
|
|
|
/*
|
2011-02-20 13:04:44 +01:00
|
|
|
Copyright (C) 2006-2011 Con Kolivas
|
2010-12-15 23:45:21 +01:00
|
|
|
Copyright (C) 1998-2003 Andrew Tridgell
|
2010-03-29 01:07:08 +02:00
|
|
|
|
|
|
|
|
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
|
2010-12-15 23:45:21 +01:00
|
|
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
2010-03-29 01:07:08 +02:00
|
|
|
*/
|
|
|
|
|
/* rzip decompression algorithm */
|
|
|
|
|
|
|
|
|
|
#include "rzip.h"
|
|
|
|
|
|
|
|
|
|
static inline uchar read_u8(void *ss, int stream)
|
|
|
|
|
{
|
|
|
|
|
uchar b;
|
2010-10-31 14:19:39 +01:00
|
|
|
|
2010-11-05 02:16:43 +01:00
|
|
|
if (unlikely(read_stream(ss, stream, &b, 1) != 1))
|
2010-03-29 01:07:08 +02:00
|
|
|
fatal("Stream read u8 failed\n");
|
|
|
|
|
return b;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static inline u32 read_u32(void *ss, int stream)
|
|
|
|
|
{
|
|
|
|
|
u32 ret;
|
|
|
|
|
|
2010-11-05 02:16:43 +01:00
|
|
|
if (unlikely(read_stream(ss, stream, (uchar *)&ret, 4) != 4))
|
2010-03-29 01:07:08 +02:00
|
|
|
fatal("Stream read u32 failed\n");
|
|
|
|
|
return ret;
|
|
|
|
|
}
|
2010-10-31 18:53:53 +01:00
|
|
|
|
2010-10-31 01:35:04 +02:00
|
|
|
/* Read a variable length of chars dependant on how big the chunk was */
|
|
|
|
|
static inline i64 read_vchars(void *ss, int stream, int length)
|
2010-03-29 01:07:08 +02:00
|
|
|
{
|
2010-10-31 01:35:04 +02:00
|
|
|
int bytes;
|
|
|
|
|
i64 s = 0;
|
2010-03-29 01:07:08 +02:00
|
|
|
|
2010-10-31 01:35:04 +02:00
|
|
|
for (bytes = 0; bytes < length; bytes++) {
|
|
|
|
|
int bits = bytes * 8;
|
2010-03-29 01:07:08 +02:00
|
|
|
|
2010-10-31 01:35:04 +02:00
|
|
|
uchar sb = read_u8(ss, stream);
|
|
|
|
|
s |= (i64)sb << bits;
|
|
|
|
|
}
|
|
|
|
|
return s;
|
2010-03-29 01:07:08 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static i64 read_header(void *ss, uchar *head)
|
|
|
|
|
{
|
2010-10-31 01:35:04 +02:00
|
|
|
int chunk_bytes = 2;
|
|
|
|
|
|
|
|
|
|
/* All chunks were unnecessarily encoded 8 bytes wide version 0.4x */
|
|
|
|
|
if (control.major_version == 0 && control.minor_version == 4)
|
|
|
|
|
chunk_bytes = 8;
|
2010-03-29 01:07:08 +02:00
|
|
|
*head = read_u8(ss, 0);
|
2010-10-31 01:35:04 +02:00
|
|
|
return read_vchars(ss, 0, chunk_bytes);
|
2010-03-29 01:07:08 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static i64 unzip_literal(void *ss, i64 len, int fd_out, uint32 *cksum)
|
|
|
|
|
{
|
2011-02-10 06:57:22 +01:00
|
|
|
i64 stream_read;
|
2010-03-29 01:07:08 +02:00
|
|
|
uchar *buf;
|
|
|
|
|
|
2010-11-05 02:16:43 +01:00
|
|
|
if (unlikely(len < 0))
|
2011-02-21 04:51:20 +01:00
|
|
|
failure("len %lld is negative in unzip_literal!\n",len);
|
2010-03-29 01:07:08 +02:00
|
|
|
|
2011-02-11 01:46:58 +01:00
|
|
|
buf = (uchar *)malloc(len);
|
2011-02-10 06:46:35 +01:00
|
|
|
if (unlikely(!buf))
|
|
|
|
|
fatal("Failed to malloc literal buffer of size %lld\n", len);
|
2010-03-29 01:07:08 +02:00
|
|
|
|
2011-02-10 06:57:22 +01:00
|
|
|
stream_read = read_stream(ss, 1, buf, len);
|
|
|
|
|
if (unlikely(stream_read == -1 ))
|
|
|
|
|
fatal("Failed to read_stream in unzip_literal\n");
|
2010-03-29 01:07:08 +02:00
|
|
|
|
2011-02-10 06:57:22 +01:00
|
|
|
if (unlikely(write_1g(fd_out, buf, (size_t)stream_read) != (ssize_t)stream_read))
|
|
|
|
|
fatal("Failed to write literal buffer of size %lld\n", stream_read);
|
|
|
|
|
|
2011-02-20 08:01:19 +01:00
|
|
|
if (!HAS_MD5)
|
|
|
|
|
*cksum = CrcUpdate(*cksum, buf, stream_read);
|
|
|
|
|
if (!NO_MD5)
|
|
|
|
|
md5_process_bytes(buf, stream_read, &control.ctx);
|
2010-03-29 01:07:08 +02:00
|
|
|
|
2011-02-10 06:46:35 +01:00
|
|
|
free(buf);
|
2011-02-10 06:57:22 +01:00
|
|
|
return stream_read;
|
2010-03-29 01:07:08 +02:00
|
|
|
}
|
|
|
|
|
|
2010-10-31 05:09:05 +01:00
|
|
|
static i64 unzip_match(void *ss, i64 len, int fd_out, int fd_hist, uint32 *cksum, int chunk_bytes)
|
2010-03-29 01:07:08 +02:00
|
|
|
{
|
2010-10-31 01:35:04 +02:00
|
|
|
i64 offset, n, total, cur_pos;
|
2011-02-16 23:30:46 +01:00
|
|
|
uchar *buf, *off_buf;
|
2010-03-29 01:07:08 +02:00
|
|
|
|
2010-11-05 02:16:43 +01:00
|
|
|
if (unlikely(len < 0))
|
2011-02-21 04:51:20 +01:00
|
|
|
failure("len %lld is negative in unzip_match!\n",len);
|
2010-03-29 01:07:08 +02:00
|
|
|
|
|
|
|
|
total = 0;
|
|
|
|
|
cur_pos = lseek(fd_out, 0, SEEK_CUR);
|
2010-11-05 02:16:43 +01:00
|
|
|
if (unlikely(cur_pos == -1))
|
2010-03-29 01:07:08 +02:00
|
|
|
fatal("Seek failed on out file in unzip_match.\n");
|
|
|
|
|
|
|
|
|
|
/* Note the offset is in a different format v0.40+ */
|
2010-10-31 01:35:04 +02:00
|
|
|
offset = read_vchars(ss, 0, chunk_bytes);
|
2010-11-05 02:16:43 +01:00
|
|
|
if (unlikely(lseek(fd_hist, cur_pos - offset, SEEK_SET) == -1))
|
2011-03-08 02:04:30 +01:00
|
|
|
fatal("Seek failed by %d from %d on history file in unzip_match\n",
|
|
|
|
|
offset, cur_pos);
|
2010-03-29 01:07:08 +02:00
|
|
|
|
2011-02-16 23:30:46 +01:00
|
|
|
buf = (uchar *)malloc(len);
|
|
|
|
|
if (unlikely(!buf))
|
|
|
|
|
fatal("Failed to malloc match buffer of size %lld\n", len);
|
|
|
|
|
off_buf = buf;
|
|
|
|
|
|
2010-03-29 01:07:08 +02:00
|
|
|
while (len) {
|
|
|
|
|
n = MIN(len, offset);
|
|
|
|
|
|
2011-02-16 23:30:46 +01:00
|
|
|
if (unlikely(read_1g(fd_hist, off_buf, (size_t)n) != (ssize_t)n))
|
2010-03-29 01:07:08 +02:00
|
|
|
fatal("Failed to read %d bytes in unzip_match\n", n);
|
|
|
|
|
|
2011-02-16 23:30:46 +01:00
|
|
|
if (unlikely(write_1g(fd_out, off_buf, (size_t)n) != (ssize_t)n))
|
2010-03-29 01:07:08 +02:00
|
|
|
fatal("Failed to write %d bytes in unzip_match\n", n);
|
|
|
|
|
|
2011-02-20 08:01:19 +01:00
|
|
|
if (!HAS_MD5)
|
|
|
|
|
*cksum = CrcUpdate(*cksum, off_buf, n);
|
|
|
|
|
if (!NO_MD5)
|
|
|
|
|
md5_process_bytes(off_buf, n, &control.ctx);
|
2010-03-29 01:07:08 +02:00
|
|
|
|
|
|
|
|
len -= n;
|
2011-02-16 23:30:46 +01:00
|
|
|
off_buf += n;
|
2010-03-29 01:07:08 +02:00
|
|
|
total += n;
|
|
|
|
|
}
|
|
|
|
|
|
2011-02-16 23:30:46 +01:00
|
|
|
free(buf);
|
|
|
|
|
|
2010-03-29 01:07:08 +02:00
|
|
|
return total;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* decompress a section of an open file. Call fatal() on error
|
|
|
|
|
return the number of bytes that have been retrieved
|
|
|
|
|
*/
|
2010-11-01 03:28:49 +01:00
|
|
|
static i64 runzip_chunk(int fd_in, int fd_out, int fd_hist, i64 expected_size, i64 tally)
|
2010-03-29 01:07:08 +02:00
|
|
|
{
|
|
|
|
|
uint32 good_cksum, cksum = 0;
|
2010-10-31 14:19:39 +01:00
|
|
|
i64 len, ofs, total = 0;
|
2010-03-29 01:07:08 +02:00
|
|
|
int l = -1, p = 0;
|
2010-11-01 03:28:49 +01:00
|
|
|
char chunk_bytes;
|
2010-10-31 05:09:05 +01:00
|
|
|
struct stat st;
|
|
|
|
|
uchar head;
|
|
|
|
|
void *ss;
|
2010-03-29 01:07:08 +02:00
|
|
|
|
|
|
|
|
/* for display of progress */
|
|
|
|
|
unsigned long divisor[] = {1,1024,1048576,1073741824U};
|
2010-10-31 14:19:39 +01:00
|
|
|
char *suffix[] = {"","KB","MB","GB"};
|
2010-03-29 01:07:08 +02:00
|
|
|
double prog_done, prog_tsize;
|
2010-10-31 14:19:39 +01:00
|
|
|
int divisor_index;
|
2010-03-29 01:07:08 +02:00
|
|
|
|
|
|
|
|
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];
|
|
|
|
|
|
2010-11-01 03:28:49 +01:00
|
|
|
/* 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 {
|
|
|
|
|
/* Read in the stored chunk byte width from the file */
|
2010-11-05 02:16:43 +01:00
|
|
|
if (unlikely(read(fd_in, &chunk_bytes, 1) != 1))
|
2010-11-01 03:28:49 +01:00
|
|
|
fatal("Failed to read chunk_bytes size in runzip_chunk\n");
|
|
|
|
|
}
|
2010-11-01 05:27:35 +01:00
|
|
|
if (!tally)
|
|
|
|
|
print_maxverbose("\nExpected size: %lld", expected_size);
|
|
|
|
|
print_maxverbose("\nChunk byte width: %d\n", chunk_bytes);
|
|
|
|
|
|
|
|
|
|
ofs = lseek(fd_in, 0, SEEK_CUR);
|
2010-11-05 02:16:43 +01:00
|
|
|
if (unlikely(ofs == -1))
|
2010-11-01 05:27:35 +01:00
|
|
|
fatal("Failed to seek input file in runzip_fd\n");
|
|
|
|
|
|
2010-11-05 04:52:14 +01:00
|
|
|
if (fstat(fd_in, &st) || st.st_size - ofs == 0)
|
2010-11-01 05:27:35 +01:00
|
|
|
return 0;
|
2010-11-01 03:28:49 +01:00
|
|
|
|
2010-03-29 01:07:08 +02:00
|
|
|
ss = open_stream_in(fd_in, NUM_STREAMS);
|
2010-11-05 02:16:43 +01:00
|
|
|
if (unlikely(!ss))
|
2010-10-31 14:19:39 +01:00
|
|
|
fatal("Failed to open_stream_in in runzip_chunk\n");
|
2010-03-29 01:07:08 +02:00
|
|
|
|
|
|
|
|
while ((len = read_header(ss, &head)) || head) {
|
|
|
|
|
switch (head) {
|
|
|
|
|
case 0:
|
|
|
|
|
total += unzip_literal(ss, len, fd_out, &cksum);
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
default:
|
2010-10-31 05:09:05 +01:00
|
|
|
total += unzip_match(ss, len, fd_out, fd_hist, &cksum, chunk_bytes);
|
2010-03-29 01:07:08 +02:00
|
|
|
break;
|
|
|
|
|
}
|
2010-10-31 05:09:05 +01:00
|
|
|
p = 100 * ((double)(tally + total) / (double)expected_size);
|
2010-11-16 11:25:32 +01:00
|
|
|
if (p / 10 != l / 10) {
|
2010-11-01 03:28:49 +01:00
|
|
|
prog_done = (double)(tally + total) / (double)divisor[divisor_index];
|
|
|
|
|
print_progress("%3d%% %9.2f / %9.2f %s\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b",
|
|
|
|
|
p, prog_done, prog_tsize, suffix[divisor_index] );
|
|
|
|
|
l = p;
|
2010-03-29 01:07:08 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2011-02-20 08:01:19 +01:00
|
|
|
if (!HAS_MD5) {
|
|
|
|
|
good_cksum = read_u32(ss, 0);
|
|
|
|
|
if (unlikely(good_cksum != cksum))
|
2011-02-21 04:51:20 +01:00
|
|
|
failure("Bad checksum: 0x%08x - expected: 0x%08x\n", cksum, good_cksum);
|
2011-02-20 08:01:19 +01:00
|
|
|
print_maxverbose("Checksum for block: 0x%08x\n", cksum);
|
|
|
|
|
}
|
2010-03-29 01:07:08 +02:00
|
|
|
|
2010-11-05 02:16:43 +01:00
|
|
|
if (unlikely(close_stream_in(ss)))
|
2010-03-29 01:07:08 +02:00
|
|
|
fatal("Failed to close stream!\n");
|
|
|
|
|
|
|
|
|
|
return total;
|
|
|
|
|
}
|
|
|
|
|
|
2010-10-31 05:17:04 +01:00
|
|
|
/* Decompress an open file. Call fatal() on error
|
2010-03-29 01:07:08 +02:00
|
|
|
return the number of bytes that have been retrieved
|
|
|
|
|
*/
|
|
|
|
|
i64 runzip_fd(int fd_in, int fd_out, int fd_hist, i64 expected_size)
|
|
|
|
|
{
|
2011-02-18 05:16:13 +01:00
|
|
|
char md5_resblock[MD5_DIGEST_SIZE];
|
2011-02-20 08:01:19 +01:00
|
|
|
char md5_stored[MD5_DIGEST_SIZE];
|
2010-03-29 01:07:08 +02:00
|
|
|
struct timeval start,end;
|
2010-10-31 05:09:05 +01:00
|
|
|
i64 total = 0;
|
2011-02-18 05:16:13 +01:00
|
|
|
|
2011-02-20 08:01:19 +01:00
|
|
|
if (!NO_MD5)
|
|
|
|
|
md5_init_ctx (&control.ctx);
|
2010-03-29 01:07:08 +02:00
|
|
|
gettimeofday(&start,NULL);
|
|
|
|
|
|
2011-03-07 07:14:07 +01:00
|
|
|
while (total < expected_size) {
|
2010-11-01 03:28:49 +01:00
|
|
|
total += runzip_chunk(fd_in, fd_out, fd_hist, expected_size, total);
|
2011-03-07 07:14:07 +01:00
|
|
|
if (STDOUT)
|
|
|
|
|
dump_tmpoutfile(fd_out);
|
|
|
|
|
}
|
2010-03-29 01:07:08 +02:00
|
|
|
|
|
|
|
|
gettimeofday(&end,NULL);
|
2010-10-31 18:53:53 +01:00
|
|
|
print_progress("\nAverage DeCompression Speed: %6.3fMB/s\n",
|
2010-03-29 01:07:08 +02:00
|
|
|
(total / 1024 / 1024) / (double)((end.tv_sec-start.tv_sec)? : 1));
|
2011-02-18 05:16:13 +01:00
|
|
|
|
2011-02-20 08:01:19 +01:00
|
|
|
if (!NO_MD5) {
|
|
|
|
|
int i,j;
|
|
|
|
|
|
|
|
|
|
md5_finish_ctx (&control.ctx, md5_resblock);
|
|
|
|
|
if (HAS_MD5) {
|
|
|
|
|
if (unlikely(lseek(fd_in, -MD5_DIGEST_SIZE, SEEK_END)) == -1)
|
|
|
|
|
fatal("Failed to seek to md5 data in runzip_fd\n");
|
|
|
|
|
if (unlikely(read(fd_in, md5_stored, MD5_DIGEST_SIZE) != MD5_DIGEST_SIZE))
|
|
|
|
|
fatal("Failed to read md5 data in runzip_fd\n");
|
|
|
|
|
for (i = 0; i < MD5_DIGEST_SIZE; i++)
|
|
|
|
|
if (md5_stored[i] != 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", md5_resblock[j] & 0xFF);
|
2011-02-21 04:51:20 +01:00
|
|
|
failure("\n");
|
2011-02-20 08:01:19 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (HASH_CHECK || MAX_VERBOSE) {
|
|
|
|
|
print_output("MD5: ");
|
|
|
|
|
for (i = 0; i < MD5_DIGEST_SIZE; i++)
|
|
|
|
|
print_output("%02x", md5_resblock[i] & 0xFF);
|
|
|
|
|
print_output("\n");
|
|
|
|
|
}
|
2011-02-20 12:29:49 +01:00
|
|
|
|
|
|
|
|
if (CHECK_FILE) {
|
|
|
|
|
FILE *md5_fstream;
|
|
|
|
|
int i, j;
|
|
|
|
|
|
|
|
|
|
memcpy(md5_stored, md5_resblock, MD5_DIGEST_SIZE);
|
2011-02-20 12:44:10 +01:00
|
|
|
if (unlikely(lseek(fd_hist, 0, SEEK_SET) == -1))
|
|
|
|
|
fatal("Failed to lseek fd_hist in runzip_fd\n");
|
|
|
|
|
if (unlikely((md5_fstream = fdopen(fd_hist, "r")) == NULL))
|
|
|
|
|
fatal("Failed to fdopen fd_hist in runzip_fd\n");
|
2011-02-20 12:29:49 +01:00
|
|
|
if (unlikely(md5_stream(md5_fstream, md5_resblock)))
|
|
|
|
|
fatal("Failed to md5_stream in runzip_fd\n");
|
|
|
|
|
/* We dont' close the file here as it's closed in main */
|
|
|
|
|
for (i = 0; i < MD5_DIGEST_SIZE; i++)
|
|
|
|
|
if (md5_stored[i] != 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", md5_resblock[j] & 0xFF);
|
2011-02-21 04:51:20 +01:00
|
|
|
failure("\n");
|
2011-02-20 12:29:49 +01:00
|
|
|
}
|
|
|
|
|
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");
|
|
|
|
|
}
|
2011-02-18 05:16:13 +01:00
|
|
|
}
|
|
|
|
|
|
2010-03-29 01:07:08 +02:00
|
|
|
return total;
|
|
|
|
|
}
|