File: runzip.c

package info (click to toggle)
lrzip 0.45-1
  • links: PTS, VCS
  • area: main
  • in suites: squeeze
  • size: 1,004 kB
  • ctags: 1,541
  • sloc: ansic: 8,237; sh: 3,092; cpp: 1,340; makefile: 195; asm: 166
file content (274 lines) | stat: -rw-r--r-- 7,035 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
/*
   Copyright (C) Andrew Tridgell 1998-2003
   Con Kolivas 2006-2009

   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, write to the Free Software
   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
/* rzip decompression algorithm */

#include "rzip.h"

static inline uchar read_u8(void *ss, int stream)
{
	uchar b;
	if (read_stream(ss, stream, &b, 1) != 1)
		fatal("Stream read u8 failed\n");
	return b;
}

static inline u16 read_u16(void *ss, int stream)
{
	u16 ret;

	if (read_stream(ss, stream, (uchar *)&ret, 2) != 2)
		fatal("Stream read u16 failed\n");
	return ret;
}

static inline u32 read_u32(void *ss, int stream)
{
	u32 ret;

	if (read_stream(ss, stream, (uchar *)&ret, 4) != 4)
		fatal("Stream read u32 failed\n");
	return ret;
}

static inline i64 read_i64(void *ss, int stream)
{
	i64 ret;

	if (read_stream(ss, stream, (uchar *)&ret, 8) != 8)
		fatal("Stream read i64 failed\n");
	return ret;
}

static u16 read_header_v03(void *ss, uchar *head)
{
	*head = read_u8(ss, 0);
	return read_u16(ss, 0);
}

static i64 read_header(void *ss, uchar *head)
{
	if (control.major_version == 0 && control.minor_version < 4)
		return read_header_v03(ss, head);
	*head = read_u8(ss, 0);
	return read_i64(ss, 0);
}

static i64 unzip_literal(void *ss, i64 len, int fd_out, uint32 *cksum)
{
	uchar *buf;

	if (len < 0)
		fatal("len %lld is negative in unzip_literal!\n",len);

	if (sizeof(long) == 4 && len > (1UL << 31))
		fatal("Unable to allocate a chunk this big on 32bit userspace. Can't decompress this file on this hardware\n");

	buf = malloc((size_t)len);
	if (!buf)
		fatal("Failed to allocate literal buffer of size %lld\n", len);

	read_stream(ss, 1, buf, len);
	if (write_1g(fd_out, buf, (size_t)len) != (ssize_t)len)
		fatal("Failed to write literal buffer of size %lld\n", len);

	*cksum = CrcUpdate(*cksum, buf, len);

	free(buf);
	return len;
}

static i64 unzip_match_v03(void *ss, i64 len, int fd_out, int fd_hist, uint32 *cksum)
{
	u32 offset;
	i64 n, total = 0;
	i64 cur_pos = lseek(fd_out, 0, SEEK_CUR);

	if (cur_pos == -1)
		fatal("Seek failed on out file in unzip_match.\n");

	offset = read_u32(ss, 0);

	if (lseek(fd_hist, cur_pos - offset, SEEK_SET) == -1)
		fatal("Seek failed by %d from %d on history file in unzip_match - %s\n",
		      offset, cur_pos, strerror(errno));

	while (len) {
		uchar *buf;
		n = MIN(len, offset);

		buf = malloc((size_t)n);
		if (!buf)
			fatal("Failed to allocate %d bytes in unzip_match\n", n);

		if (read_1g(fd_hist, buf, (size_t)n) != (ssize_t)n)
			fatal("Failed to read %d bytes in unzip_match\n", n);

		if (write_1g(fd_out, buf, (size_t)n) != (ssize_t)n)
			fatal("Failed to write %d bytes in unzip_match\n", n);

		*cksum = CrcUpdate(*cksum, buf, n);

		len -= n;
		free(buf);
		total += n;
	}

	return total;
}

static i64 unzip_match(void *ss, i64 len, int fd_out, int fd_hist, uint32 *cksum)
{
	i64 cur_pos;
	i64 offset, n, total;

	if (len < 0)
		fatal("len %lld is negative in unzip_match!\n",len);

	if (control.major_version == 0 && control.minor_version < 4)
		return unzip_match_v03(ss, len, fd_out, fd_hist, cksum);

	total = 0;
	cur_pos = lseek(fd_out, 0, SEEK_CUR);
	if (cur_pos == -1)
		fatal("Seek failed on out file in unzip_match.\n");

	/* Note the offset is in a different format v0.40+ */
	offset = read_i64(ss, 0);
	if (lseek(fd_hist, cur_pos - offset, SEEK_SET) == -1)
		fatal("Seek failed by %d from %d on history file in unzip_match - %s\n",
		      offset, cur_pos, strerror(errno));

	while (len) {
		uchar *buf;
		n = MIN(len, offset);

		buf = malloc((size_t)n);
		if (!buf)
			fatal("Failed to allocate %d bytes in unzip_match\n", n);

		if (read_1g(fd_hist, buf, (size_t)n) != (ssize_t)n)
			fatal("Failed to read %d bytes in unzip_match\n", n);

		if (write_1g(fd_out, buf, (size_t)n) != (ssize_t)n)
			fatal("Failed to write %d bytes in unzip_match\n", n);

		*cksum = CrcUpdate(*cksum, buf, n);

		len -= n;
		free(buf);
		total += n;
	}

	return total;
}

/* decompress a section of an open file. Call fatal() on error
   return the number of bytes that have been retrieved
 */
static i64 runzip_chunk(int fd_in, int fd_out, int fd_hist, i64 expected_size, i64 tally)
{
	uchar head;
	i64 len;
	struct stat st;
	void *ss;
	i64 ofs;
	i64 total = 0;
	uint32 good_cksum, cksum = 0;
	int l = -1, p = 0;

	/* for display of progress */
	char *suffix[] = {"","KB","MB","GB"};
	unsigned long divisor[] = {1,1024,1048576,1073741824U};
	int divisor_index;
	double prog_done, prog_tsize;

	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];

	ofs = lseek(fd_in, 0, SEEK_CUR);
	if (ofs == -1)
		fatal("Failed to seek input file in runzip_fd\n");

	if (fstat(fd_in, &st) != 0 || st.st_size-ofs == 0)
		return 0;

	ss = open_stream_in(fd_in, NUM_STREAMS);
	if (!ss)
		fatal(NULL);

	while ((len = read_header(ss, &head)) || head) {
		switch (head) {
			case 0:
				total += unzip_literal(ss, len, fd_out, &cksum);
				break;

			default:
				total += unzip_match(ss, len, fd_out, fd_hist, &cksum);
				break;
		}
		p = 100 * ((double)(tally+total) / (double)expected_size);
		if (control.flags & FLAG_SHOW_PROGRESS) {
			if ( p != l )  {
				prog_done = (double)(tally+total) / (double)divisor[divisor_index];
				fprintf(stderr, "%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] );
				fflush(stderr);
				l = p;
			}
		}
	}

	good_cksum = read_u32(ss, 0);
	if (good_cksum != cksum)
		fatal("Bad checksum 0x%08x - expected 0x%08x\n", cksum, good_cksum);

	if (close_stream_in(ss) != 0)
		fatal("Failed to close stream!\n");

	return total;
}

/* decompress a open file. Call fatal() on error
   return the number of bytes that have been retrieved
 */

i64 runzip_fd(int fd_in, int fd_out, int fd_hist, i64 expected_size)
{
	i64 total = 0;
	struct timeval start,end;

	gettimeofday(&start,NULL);

	while (total < expected_size)
		total += runzip_chunk(fd_in, fd_out, fd_hist, expected_size, total);

	gettimeofday(&end,NULL);
	if (control.flags & FLAG_SHOW_PROGRESS)
		fprintf(stderr, "\nAverage DeCompression Speed: %6.3fMB/s\n",
			(total / 1024 / 1024) / (double)((end.tv_sec-start.tv_sec)? : 1));
	return total;
}