Commit | Line | Data |
---|---|---|
ad9d2716 DG |
1 | /* |
2 | * Copyright 2007 David Gibson, IBM Corporation. | |
3 | * Based on earlier work, Copyright (C) Paul Mackerras 1997. | |
4 | * | |
5 | * This program is free software; you can redistribute it and/or | |
6 | * modify it under the terms of the GNU General Public License | |
7 | * as published by the Free Software Foundation; either version | |
8 | * 2 of the License, or (at your option) any later version. | |
9 | */ | |
10 | ||
11 | #include <stddef.h> | |
12 | #include "string.h" | |
13 | #include "stdio.h" | |
14 | #include "ops.h" | |
15 | #include "gunzip_util.h" | |
16 | ||
ad9d2716 DG |
17 | #define HEAD_CRC 2 |
18 | #define EXTRA_FIELD 4 | |
19 | #define ORIG_NAME 8 | |
20 | #define COMMENT 0x10 | |
21 | #define RESERVED 0xe0 | |
22 | ||
7850ad5c DG |
23 | /** |
24 | * gunzip_start - prepare to decompress gzip data | |
25 | * @state: decompressor state structure to be initialized | |
26 | * @src: buffer containing gzip compressed or uncompressed data | |
27 | * @srclen: size in bytes of the buffer at src | |
28 | * | |
29 | * If the buffer at @src contains a gzip header, this function | |
30 | * initializes zlib to decompress the data, storing the decompression | |
31 | * state in @state. The other functions in this file can then be used | |
32 | * to decompress data from the gzipped stream. | |
33 | * | |
34 | * If the buffer at @src does not contain a gzip header, it is assumed | |
35 | * to contain uncompressed data. The buffer information is recorded | |
36 | * in @state and the other functions in this file will simply copy | |
37 | * data from the uncompressed data stream at @src. | |
38 | * | |
39 | * Any errors, such as bad compressed data, cause an error to be | |
40 | * printed an the platform's exit() function to be called. | |
41 | */ | |
ad9d2716 DG |
42 | void gunzip_start(struct gunzip_state *state, void *src, int srclen) |
43 | { | |
44 | char *hdr = src; | |
45 | int hdrlen = 0; | |
46 | ||
47 | memset(state, 0, sizeof(*state)); | |
48 | ||
49 | /* Check for gzip magic number */ | |
50 | if ((hdr[0] == 0x1f) && (hdr[1] == 0x8b)) { | |
51 | /* gzip data, initialize zlib parameters */ | |
52 | int r, flags; | |
53 | ||
54 | state->s.workspace = state->scratch; | |
6a923216 MM |
55 | if (zlib_inflate_workspacesize() > sizeof(state->scratch)) |
56 | fatal("insufficient scratch space for gunzip\n\r"); | |
ad9d2716 DG |
57 | |
58 | /* skip header */ | |
59 | hdrlen = 10; | |
60 | flags = hdr[3]; | |
6a923216 MM |
61 | if (hdr[2] != Z_DEFLATED || (flags & RESERVED) != 0) |
62 | fatal("bad gzipped data\n\r"); | |
ad9d2716 DG |
63 | if ((flags & EXTRA_FIELD) != 0) |
64 | hdrlen = 12 + hdr[10] + (hdr[11] << 8); | |
65 | if ((flags & ORIG_NAME) != 0) | |
66 | while (hdr[hdrlen++] != 0) | |
67 | ; | |
68 | if ((flags & COMMENT) != 0) | |
69 | while (hdr[hdrlen++] != 0) | |
70 | ; | |
71 | if ((flags & HEAD_CRC) != 0) | |
72 | hdrlen += 2; | |
6a923216 MM |
73 | if (hdrlen >= srclen) |
74 | fatal("gunzip_start: ran out of data in header\n\r"); | |
ad9d2716 DG |
75 | |
76 | r = zlib_inflateInit2(&state->s, -MAX_WBITS); | |
6a923216 MM |
77 | if (r != Z_OK) |
78 | fatal("inflateInit2 returned %d\n\r", r); | |
ad9d2716 DG |
79 | } |
80 | ||
e47654d0 | 81 | state->s.total_in = hdrlen; |
ad9d2716 DG |
82 | state->s.next_in = src + hdrlen; |
83 | state->s.avail_in = srclen - hdrlen; | |
84 | } | |
85 | ||
7850ad5c DG |
86 | /** |
87 | * gunzip_partial - extract bytes from a gzip data stream | |
88 | * @state: gzip state structure previously initialized by gunzip_start() | |
89 | * @dst: buffer to store extracted data | |
90 | * @dstlen: maximum number of bytes to extract | |
91 | * | |
92 | * This function extracts at most @dstlen bytes from the data stream | |
93 | * previously associated with @state by gunzip_start(), decompressing | |
94 | * if necessary. Exactly @dstlen bytes are extracted unless the data | |
95 | * stream doesn't contain enough bytes, in which case the entire | |
96 | * remainder of the stream is decompressed. | |
97 | * | |
98 | * Returns the actual number of bytes extracted. If any errors occur, | |
99 | * such as a corrupted compressed stream, an error is printed an the | |
100 | * platform's exit() function is called. | |
101 | */ | |
ad9d2716 DG |
102 | int gunzip_partial(struct gunzip_state *state, void *dst, int dstlen) |
103 | { | |
104 | int len; | |
105 | ||
106 | if (state->s.workspace) { | |
107 | /* gunzipping */ | |
108 | int r; | |
109 | ||
110 | state->s.next_out = dst; | |
111 | state->s.avail_out = dstlen; | |
112 | r = zlib_inflate(&state->s, Z_FULL_FLUSH); | |
6a923216 MM |
113 | if (r != Z_OK && r != Z_STREAM_END) |
114 | fatal("inflate returned %d msg: %s\n\r", r, state->s.msg); | |
ad9d2716 DG |
115 | len = state->s.next_out - (unsigned char *)dst; |
116 | } else { | |
117 | /* uncompressed image */ | |
118 | len = min(state->s.avail_in, (unsigned)dstlen); | |
119 | memcpy(dst, state->s.next_in, len); | |
120 | state->s.next_in += len; | |
121 | state->s.avail_in -= len; | |
122 | } | |
123 | return len; | |
124 | } | |
125 | ||
7850ad5c DG |
126 | /** |
127 | * gunzip_exactly - extract a fixed number of bytes from a gzip data stream | |
128 | * @state: gzip state structure previously initialized by gunzip_start() | |
129 | * @dst: buffer to store extracted data | |
130 | * @dstlen: number of bytes to extract | |
131 | * | |
132 | * This function extracts exactly @dstlen bytes from the data stream | |
133 | * previously associated with @state by gunzip_start(), decompressing | |
134 | * if necessary. | |
135 | * | |
136 | * If there are less @dstlen bytes available in the data stream, or if | |
137 | * any other errors occur, such as a corrupted compressed stream, an | |
138 | * error is printed an the platform's exit() function is called. | |
139 | */ | |
ad9d2716 DG |
140 | void gunzip_exactly(struct gunzip_state *state, void *dst, int dstlen) |
141 | { | |
142 | int len; | |
143 | ||
144 | len = gunzip_partial(state, dst, dstlen); | |
6a923216 | 145 | if (len < dstlen) |
3c5f6162 MM |
146 | fatal("\n\rgunzip_exactly: ran out of data!" |
147 | " Wanted %d, got %d.\n\r", dstlen, len); | |
ad9d2716 DG |
148 | } |
149 | ||
7850ad5c DG |
150 | /** |
151 | * gunzip_discard - discard bytes from a gzip data stream | |
152 | * @state: gzip state structure previously initialized by gunzip_start() | |
153 | * @len: number of bytes to discard | |
154 | * | |
155 | * This function extracts, then discards exactly @len bytes from the | |
156 | * data stream previously associated with @state by gunzip_start(). | |
157 | * Subsequent gunzip_partial(), gunzip_exactly() or gunzip_finish() | |
158 | * calls will extract the data following the discarded bytes in the | |
159 | * data stream. | |
160 | * | |
161 | * If there are less @len bytes available in the data stream, or if | |
162 | * any other errors occur, such as a corrupted compressed stream, an | |
163 | * error is printed an the platform's exit() function is called. | |
164 | */ | |
ad9d2716 DG |
165 | void gunzip_discard(struct gunzip_state *state, int len) |
166 | { | |
167 | static char discard_buf[128]; | |
168 | ||
169 | while (len > sizeof(discard_buf)) { | |
170 | gunzip_exactly(state, discard_buf, sizeof(discard_buf)); | |
171 | len -= sizeof(discard_buf); | |
172 | } | |
173 | ||
174 | if (len > 0) | |
175 | gunzip_exactly(state, discard_buf, len); | |
176 | } | |
177 | ||
7850ad5c DG |
178 | /** |
179 | * gunzip_finish - extract all remaining bytes from a gzip data stream | |
180 | * @state: gzip state structure previously initialized by gunzip_start() | |
181 | * @dst: buffer to store extracted data | |
182 | * @dstlen: maximum number of bytes to extract | |
183 | * | |
184 | * This function extracts all remaining data, or at most @dstlen | |
185 | * bytes, from the stream previously associated with @state by | |
186 | * gunzip_start(). zlib is then shut down, so it is an error to use | |
187 | * any of the functions in this file on @state until it is | |
188 | * re-initialized with another call to gunzip_start(). | |
189 | * | |
190 | * If any errors occur, such as a corrupted compressed stream, an | |
191 | * error is printed an the platform's exit() function is called. | |
192 | */ | |
ad9d2716 DG |
193 | int gunzip_finish(struct gunzip_state *state, void *dst, int dstlen) |
194 | { | |
195 | int len; | |
196 | ||
51a505d7 MM |
197 | len = gunzip_partial(state, dst, dstlen); |
198 | ||
ad9d2716 | 199 | if (state->s.workspace) { |
ad9d2716 | 200 | zlib_inflateEnd(&state->s); |
ad9d2716 DG |
201 | } |
202 | ||
203 | return len; | |
204 | } |