OpenWrt – Blame information for rev 1
?pathlinks?
Rev | Author | Line No. | Line |
---|---|---|---|
1 | office | 1 | /* |
2 | * A tool for reading the zlib compressed calibration data |
||
3 | * found in AVM Fritz!Box based devices). |
||
4 | * |
||
5 | * Copyright (c) 2017 Christian Lamparter <chunkeey@googlemail.com> |
||
6 | * |
||
7 | * Based on zpipe, which is an example of proper use of zlib's inflate(). |
||
8 | * that is Not copyrighted -- provided to the public domain |
||
9 | * Version 1.4 11 December 2005 Mark Adler |
||
10 | * |
||
11 | * This program is free software; you can redistribute it and/or modify |
||
12 | * it under the terms of the GNU General Public License as published by |
||
13 | * the Free Software Foundation; either version 2 of the License, or |
||
14 | * (at your option) any later version. |
||
15 | * |
||
16 | * This program is distributed in the hope that it will be useful, |
||
17 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
||
18 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||
19 | * GNU General Public License for more details. |
||
20 | * |
||
21 | * You should have received a copy of the GNU General Public License along |
||
22 | * with this program; if not, write to the Free Software Foundation, Inc., |
||
23 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. |
||
24 | */ |
||
25 | |||
26 | #include <stdio.h> |
||
27 | #include <string.h> |
||
28 | #include <assert.h> |
||
29 | #include <unistd.h> |
||
30 | #include <stdint.h> |
||
31 | #include <stdlib.h> |
||
32 | #include <endian.h> |
||
33 | #include <errno.h> |
||
34 | #include "zlib.h" |
||
35 | |||
36 | #define CHUNK 1024 |
||
37 | |||
38 | static inline size_t special_min(size_t a, size_t b) |
||
39 | { |
||
40 | return a == 0 ? b : (a < b ? a : b); |
||
41 | } |
||
42 | |||
43 | /* Decompress from file source to file dest until stream ends or EOF. |
||
44 | inf() returns Z_OK on success, Z_MEM_ERROR if memory could not be |
||
45 | allocated for processing, Z_DATA_ERROR if the deflate data is |
||
46 | invalid or incomplete, Z_VERSION_ERROR if the version of zlib.h and |
||
47 | the version of the library linked do not match, or Z_ERRNO if there |
||
48 | is an error reading or writing the files. */ |
||
49 | static int inf(FILE *source, FILE *dest, size_t limit, size_t skip) |
||
50 | { |
||
51 | int ret; |
||
52 | size_t have; |
||
53 | z_stream strm; |
||
54 | unsigned char in[CHUNK]; |
||
55 | unsigned char out[CHUNK]; |
||
56 | |||
57 | /* allocate inflate state */ |
||
58 | strm.zalloc = Z_NULL; |
||
59 | strm.zfree = Z_NULL; |
||
60 | strm.opaque = Z_NULL; |
||
61 | strm.avail_in = 0; |
||
62 | strm.next_in = Z_NULL; |
||
63 | ret = inflateInit(&strm); |
||
64 | if (ret != Z_OK) |
||
65 | return ret; |
||
66 | |||
67 | /* decompress until deflate stream ends or end of file */ |
||
68 | do { |
||
69 | strm.avail_in = fread(in, 1, CHUNK, source); |
||
70 | if (ferror(source)) { |
||
71 | (void)inflateEnd(&strm); |
||
72 | return Z_ERRNO; |
||
73 | } |
||
74 | if (strm.avail_in == 0) |
||
75 | break; |
||
76 | strm.next_in = in; |
||
77 | |||
78 | /* run inflate() on input until output buffer not full */ |
||
79 | do { |
||
80 | strm.avail_out = CHUNK; |
||
81 | strm.next_out = out; |
||
82 | ret = inflate(&strm, Z_NO_FLUSH); |
||
83 | assert(ret != Z_STREAM_ERROR); /* state not clobbered */ |
||
84 | switch (ret) { |
||
85 | case Z_NEED_DICT: |
||
86 | ret = Z_DATA_ERROR; /* and fall through */ |
||
87 | case Z_DATA_ERROR: |
||
88 | case Z_MEM_ERROR: |
||
89 | (void)inflateEnd(&strm); |
||
90 | return ret; |
||
91 | } |
||
92 | have = special_min(limit, CHUNK - strm.avail_out) - skip; |
||
93 | if (fwrite(&out[skip], have, 1, dest) != 1 || ferror(dest)) { |
||
94 | (void)inflateEnd(&strm); |
||
95 | return Z_ERRNO; |
||
96 | } |
||
97 | skip = 0; |
||
98 | limit -= have; |
||
99 | } while (strm.avail_out == 0 && limit > 0); |
||
100 | |||
101 | /* done when inflate() says it's done */ |
||
102 | } while (ret != Z_STREAM_END && limit > 0); |
||
103 | |||
104 | /* clean up and return */ |
||
105 | (void)inflateEnd(&strm); |
||
106 | return (limit == 0 ? Z_OK : (ret == Z_STREAM_END ? Z_OK : Z_DATA_ERROR)); |
||
107 | } |
||
108 | |||
109 | /* report a zlib or i/o error */ |
||
110 | static void zerr(int ret) |
||
111 | { |
||
112 | switch (ret) { |
||
113 | case Z_ERRNO: |
||
114 | if (ferror(stdin)) |
||
115 | fputs("error reading stdin\n", stderr); |
||
116 | if (ferror(stdout)) |
||
117 | fputs("error writing stdout\n", stderr); |
||
118 | break; |
||
119 | case Z_STREAM_ERROR: |
||
120 | fputs("invalid compression level\n", stderr); |
||
121 | break; |
||
122 | case Z_DATA_ERROR: |
||
123 | fputs("invalid or incomplete deflate data\n", stderr); |
||
124 | break; |
||
125 | case Z_MEM_ERROR: |
||
126 | fputs("out of memory\n", stderr); |
||
127 | break; |
||
128 | case Z_VERSION_ERROR: |
||
129 | fputs("zlib version mismatch!\n", stderr); |
||
130 | } |
||
131 | } |
||
132 | |||
133 | static unsigned int get_num(char *str) |
||
134 | { |
||
135 | if (!strncmp("0x", str, 2)) |
||
136 | return strtoul(str+2, NULL, 16); |
||
137 | else |
||
138 | return strtoul(str, NULL, 10); |
||
139 | } |
||
140 | |||
141 | static void usage(void) |
||
142 | { |
||
143 | fprintf(stderr, "Usage: fritz_cal_extract [-s seek offset] [-i skip] [-o output file] [-l limit] [infile] -e entry_id\n" |
||
144 | "Finds and extracts zlib compressed calibration data in the EVA loader\n"); |
||
145 | exit(EXIT_FAILURE); |
||
146 | } |
||
147 | |||
148 | struct cal_entry { |
||
149 | uint16_t id; |
||
150 | uint16_t len; |
||
151 | } __attribute__((packed)); |
||
152 | |||
153 | /* compress or decompress from stdin to stdout */ |
||
154 | int main(int argc, char **argv) |
||
155 | { |
||
156 | struct cal_entry cal = { .len = 0 }; |
||
157 | FILE *in = stdin; |
||
158 | FILE *out = stdout; |
||
159 | size_t limit = 0, skip = 0; |
||
160 | int initial_offset = 0; |
||
161 | int entry = -1; |
||
162 | int ret; |
||
163 | int opt; |
||
164 | |||
165 | while ((opt = getopt(argc, argv, "s:e:o:l:i:")) != -1) { |
||
166 | switch (opt) { |
||
167 | case 's': |
||
168 | initial_offset = (int)get_num(optarg); |
||
169 | if (errno) { |
||
170 | perror("Failed to parse seek offset"); |
||
171 | goto out_bad; |
||
172 | } |
||
173 | break; |
||
174 | case 'e': |
||
175 | entry = (int) htobe16(get_num(optarg)); |
||
176 | if (errno) { |
||
177 | perror("Failed to entry id"); |
||
178 | goto out_bad; |
||
179 | } |
||
180 | break; |
||
181 | case 'o': |
||
182 | out = fopen(optarg, "w"); |
||
183 | if (!out) { |
||
184 | perror("Failed to create output file"); |
||
185 | goto out_bad; |
||
186 | } |
||
187 | break; |
||
188 | case 'l': |
||
189 | limit = (size_t)get_num(optarg); |
||
190 | if (errno) { |
||
191 | perror("Failed to parse limit"); |
||
192 | goto out_bad; |
||
193 | } |
||
194 | break; |
||
195 | case 'i': |
||
196 | skip = (size_t)get_num(optarg); |
||
197 | if (errno) { |
||
198 | perror("Failed to parse skip"); |
||
199 | goto out_bad; |
||
200 | } |
||
201 | break; |
||
202 | default: /* '?' */ |
||
203 | usage(); |
||
204 | } |
||
205 | } |
||
206 | |||
207 | if (entry == -1) |
||
208 | usage(); |
||
209 | |||
210 | if (argc > 1 && optind <= argc) { |
||
211 | in = fopen(argv[optind], "r"); |
||
212 | if (!in) { |
||
213 | perror("Failed to create output file"); |
||
214 | goto out_bad; |
||
215 | } |
||
216 | } |
||
217 | |||
218 | if (initial_offset) { |
||
219 | ret = fseek(in, initial_offset, SEEK_CUR); |
||
220 | if (ret) { |
||
221 | perror("Failed to seek to calibration table"); |
||
222 | goto out_bad; |
||
223 | } |
||
224 | } |
||
225 | |||
226 | do { |
||
227 | ret = fseek(in, be16toh(cal.len), SEEK_CUR); |
||
228 | if (feof(in)) { |
||
229 | fprintf(stderr, "Reached end of file, but didn't find the matching entry\n"); |
||
230 | goto out_bad; |
||
231 | } else if (ferror(in)) { |
||
232 | perror("Failure during seek"); |
||
233 | goto out_bad; |
||
234 | } |
||
235 | |||
236 | ret = fread(&cal, 1, sizeof cal, in); |
||
237 | if (ret != sizeof cal) |
||
238 | goto out_bad; |
||
239 | } while (entry != cal.id || cal.id == 0xffff); |
||
240 | |||
241 | if (cal.id == 0xffff) { |
||
242 | fprintf(stderr, "Reached end of filesystem, but didn't find the matching entry\n"); |
||
243 | goto out_bad; |
||
244 | } |
||
245 | |||
246 | ret = inf(in, out, limit, skip); |
||
247 | if (ret == Z_OK) |
||
248 | goto out; |
||
249 | |||
250 | zerr(ret); |
||
251 | |||
252 | out_bad: |
||
253 | ret = EXIT_FAILURE; |
||
254 | |||
255 | out: |
||
256 | fclose(in); |
||
257 | fclose(out); |
||
258 | return ret; |
||
259 | } |