otrx: change command line API to start with a mode
[openwrt/openwrt.git] / package / utils / otrx / src / otrx.c
1 /*
2 * otrx
3 *
4 * Copyright (C) 2015 Rafał Miłecki <zajec5@gmail.com>
5 *
6 * This program is free software; you can redistribute it and/or modify it
7 * under the terms of the GNU General Public License as published by the Free
8 * Software Foundation; either version 2 of the License, or (at your option)
9 * any later version.
10 */
11
12 #include <byteswap.h>
13 #include <errno.h>
14 #include <stdint.h>
15 #include <stdio.h>
16 #include <stdlib.h>
17 #include <string.h>
18 #include <unistd.h>
19
20 #if __BYTE_ORDER == __BIG_ENDIAN
21 #define cpu_to_le32(x) bswap_32(x)
22 #define le32_to_cpu(x) bswap_32(x)
23 #elif __BYTE_ORDER == __LITTLE_ENDIAN
24 #define cpu_to_le32(x) (x)
25 #define le32_to_cpu(x) (x)
26 #else
27 #error "Unsupported endianness"
28 #endif
29
30 #define TRX_MAGIC 0x30524448
31 #define TRX_FLAGS_OFFSET 12
32 #define TRX_MAX_PARTS 3
33
34 struct trx_header {
35 uint32_t magic;
36 uint32_t length;
37 uint32_t crc32;
38 uint16_t flags;
39 uint16_t version;
40 uint32_t offset[3];
41 };
42
43 char *trx_path;
44 size_t trx_offset = 0;
45 char *partition[TRX_MAX_PARTS] = {};
46
47 /**************************************************
48 * CRC32
49 **************************************************/
50
51 uint32_t otrx_crc32(uint8_t *buf, size_t len) {
52 static const uint32_t t[] = {
53 0x00000000, 0x77073096, 0xee0e612c, 0x990951ba,
54 0x076dc419, 0x706af48f, 0xe963a535, 0x9e6495a3,
55 0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988,
56 0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91,
57 0x1db71064, 0x6ab020f2, 0xf3b97148, 0x84be41de,
58 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7,
59 0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec,
60 0x14015c4f, 0x63066cd9, 0xfa0f3d63, 0x8d080df5,
61 0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172,
62 0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b,
63 0x35b5a8fa, 0x42b2986c, 0xdbbbc9d6, 0xacbcf940,
64 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59,
65 0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116,
66 0x21b4f4b5, 0x56b3c423, 0xcfba9599, 0xb8bda50f,
67 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924,
68 0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d,
69 0x76dc4190, 0x01db7106, 0x98d220bc, 0xefd5102a,
70 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433,
71 0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818,
72 0x7f6a0dbb, 0x086d3d2d, 0x91646c97, 0xe6635c01,
73 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e,
74 0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457,
75 0x65b0d9c6, 0x12b7e950, 0x8bbeb8ea, 0xfcb9887c,
76 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65,
77 0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2,
78 0x4adfa541, 0x3dd895d7, 0xa4d1c46d, 0xd3d6f4fb,
79 0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0,
80 0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9,
81 0x5005713c, 0x270241aa, 0xbe0b1010, 0xc90c2086,
82 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f,
83 0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4,
84 0x59b33d17, 0x2eb40d81, 0xb7bd5c3b, 0xc0ba6cad,
85 0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a,
86 0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683,
87 0xe3630b12, 0x94643b84, 0x0d6d6a3e, 0x7a6a5aa8,
88 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1,
89 0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe,
90 0xf762575d, 0x806567cb, 0x196c3671, 0x6e6b06e7,
91 0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc,
92 0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5,
93 0xd6d6a3e8, 0xa1d1937e, 0x38d8c2c4, 0x4fdff252,
94 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b,
95 0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60,
96 0xdf60efc3, 0xa867df55, 0x316e8eef, 0x4669be79,
97 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236,
98 0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f,
99 0xc5ba3bbe, 0xb2bd0b28, 0x2bb45a92, 0x5cb36a04,
100 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d,
101 0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a,
102 0x9c0906a9, 0xeb0e363f, 0x72076785, 0x05005713,
103 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38,
104 0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21,
105 0x86d3d2d4, 0xf1d4e242, 0x68ddb3f8, 0x1fda836e,
106 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777,
107 0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c,
108 0x8f659eff, 0xf862ae69, 0x616bffd3, 0x166ccf45,
109 0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2,
110 0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db,
111 0xaed16a4a, 0xd9d65adc, 0x40df0b66, 0x37d83bf0,
112 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9,
113 0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6,
114 0xbad03605, 0xcdd70693, 0x54de5729, 0x23d967bf,
115 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94,
116 0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d,
117 };
118 uint32_t crc = 0xffffffff;
119
120 while (len) {
121 crc = t[(crc ^ *buf) & 0xff] ^ (crc >> 8);
122 buf++;
123 len--;
124 }
125
126 return crc;
127 }
128
129 /**************************************************
130 * Check
131 **************************************************/
132
133 static void otrx_check_parse_options(int argc, char **argv) {
134 int c;
135
136 while ((c = getopt(argc, argv, "o:")) != -1) {
137 switch (c) {
138 case 'o':
139 trx_offset = atoi(optarg);
140 break;
141 }
142 }
143 }
144
145 static int otrx_check(int argc, char **argv) {
146 FILE *trx;
147 struct trx_header hdr;
148 size_t bytes, length;
149 uint8_t *buf;
150 uint32_t crc32;
151 int err = 0;
152
153 if (argc < 3) {
154 fprintf(stderr, "No TRX file passed\n");
155 err = -EINVAL;
156 goto out;
157 }
158 trx_path = argv[2];
159
160 optind = 3;
161 otrx_check_parse_options(argc, argv);
162
163 trx = fopen(trx_path, "r");
164 if (!trx) {
165 fprintf(stderr, "Couldn't open %s\n", trx_path);
166 err = -EACCES;
167 goto out;
168 }
169
170 fseek(trx, trx_offset, SEEK_SET);
171 bytes = fread(&hdr, 1, sizeof(hdr), trx);
172 if (bytes != sizeof(hdr)) {
173 fprintf(stderr, "Couldn't read %s header\n", trx_path);
174 err = -EIO;
175 goto err_close;
176 }
177
178 if (le32_to_cpu(hdr.magic) != TRX_MAGIC) {
179 fprintf(stderr, "Invalid TRX magic: 0x%08x\n", le32_to_cpu(hdr.magic));
180 err = -EINVAL;
181 goto err_close;
182 }
183
184 length = le32_to_cpu(hdr.length);
185 if (length < sizeof(hdr)) {
186 fprintf(stderr, "Length read from TRX too low (%zu B)\n", length);
187 err = -EINVAL;
188 goto err_close;
189 }
190
191 buf = malloc(length);
192 if (!buf) {
193 fprintf(stderr, "Couldn't alloc %zd B buffer\n", length);
194 err = -ENOMEM;
195 goto err_close;
196 }
197
198 fseek(trx, trx_offset, SEEK_SET);
199 bytes = fread(buf, 1, length, trx);
200 if (bytes != length) {
201 fprintf(stderr, "Couldn't read %zd B of data from %s\n", length, trx_path);
202 err = -ENOMEM;
203 goto err_free_buf;
204 }
205
206 crc32 = otrx_crc32(buf + TRX_FLAGS_OFFSET, length - TRX_FLAGS_OFFSET);
207 if (crc32 != le32_to_cpu(hdr.crc32)) {
208 fprintf(stderr, "Invalid data crc32: 0x%08x instead of 0x%08x\n", crc32, le32_to_cpu(hdr.crc32));
209 err = -EINVAL;
210 goto err_free_buf;
211 }
212
213 printf("Found a valid TRX version %d\n", le32_to_cpu(hdr.version));
214
215 err_free_buf:
216 free(buf);
217 err_close:
218 fclose(trx);
219 out:
220 return err;
221 }
222
223 /**************************************************
224 * Extract
225 **************************************************/
226
227 static void otrx_extract_parse_options(int argc, char **argv) {
228 int c;
229
230 while ((c = getopt(argc, argv, "c:e:o:1:2:3:")) != -1) {
231 switch (c) {
232 case 'o':
233 trx_offset = atoi(optarg);
234 break;
235 case '1':
236 partition[0] = optarg;
237 break;
238 case '2':
239 partition[1] = optarg;
240 break;
241 case '3':
242 partition[2] = optarg;
243 break;
244 }
245 }
246 }
247
248 static int otrx_extract_copy(FILE *trx, size_t offset, size_t length, char *out_path) {
249 FILE *out;
250 size_t bytes;
251 uint8_t *buf;
252 int err = 0;
253
254 out = fopen(out_path, "w");
255 if (!out) {
256 fprintf(stderr, "Couldn't open %s\n", out_path);
257 err = -EACCES;
258 goto out;
259 }
260
261 buf = malloc(length);
262 if (!buf) {
263 fprintf(stderr, "Couldn't alloc %zu B buffer\n", length);
264 err = -ENOMEM;
265 goto err_close;
266 }
267
268 fseek(trx, offset, SEEK_SET);
269 bytes = fread(buf, 1, length, trx);
270 if (bytes != length) {
271 fprintf(stderr, "Couldn't read %zu B of data from %s\n", length, trx_path);
272 err = -ENOMEM;
273 goto err_free_buf;
274 };
275
276 bytes = fwrite(buf, 1, length, out);
277 if (bytes != length) {
278 fprintf(stderr, "Couldn't write %zu B to %s\n", length, out_path);
279 err = -ENOMEM;
280 goto err_free_buf;
281 }
282
283 printf("Extracted 0x%zx bytes into %s\n", length, out_path);
284
285 err_free_buf:
286 free(buf);
287 err_close:
288 fclose(out);
289 out:
290 return err;
291 }
292
293 static int otrx_extract(int argc, char **argv) {
294 FILE *trx;
295 struct trx_header hdr;
296 size_t bytes;
297 int i;
298 int err = 0;
299
300 if (argc < 3) {
301 fprintf(stderr, "No TRX file passed\n");
302 err = -EINVAL;
303 goto out;
304 }
305 trx_path = argv[2];
306
307 optind = 3;
308 otrx_extract_parse_options(argc, argv);
309
310 trx = fopen(trx_path, "r");
311 if (!trx) {
312 fprintf(stderr, "Couldn't open %s\n", trx_path);
313 err = -EACCES;
314 goto out;
315 }
316
317 fseek(trx, trx_offset, SEEK_SET);
318 bytes = fread(&hdr, 1, sizeof(hdr), trx);
319 if (bytes != sizeof(hdr)) {
320 fprintf(stderr, "Couldn't read %s header\n", trx_path);
321 err = -EIO;
322 goto err_close;
323 }
324
325 if (le32_to_cpu(hdr.magic) != TRX_MAGIC) {
326 fprintf(stderr, "Invalid TRX magic: 0x%08x\n", le32_to_cpu(hdr.magic));
327 err = -EINVAL;
328 goto err_close;
329 }
330
331 for (i = 0; i < TRX_MAX_PARTS; i++) {
332 size_t length;
333
334 if (!partition[i])
335 continue;
336 if (!hdr.offset[i]) {
337 printf("TRX doesn't contain partition %d, can't extract %s\n", i + 1, partition[i]);
338 continue;
339 }
340
341 if (i + 1 >= TRX_MAX_PARTS || !hdr.offset[i + 1])
342 length = le32_to_cpu(hdr.length) - le32_to_cpu(hdr.offset[i]);
343 else
344 length = le32_to_cpu(hdr.offset[i + 1]) - le32_to_cpu(hdr.offset[i]);
345
346 otrx_extract_copy(trx, trx_offset + le32_to_cpu(hdr.offset[i]), length, partition[i]);
347 }
348
349 err_close:
350 fclose(trx);
351 out:
352 return err;
353 }
354
355 /**************************************************
356 * Start
357 **************************************************/
358
359 static void usage() {
360 printf("Usage:\n");
361 printf("\n");
362 printf("Checking TRX file:\n");
363 printf("\totrx check <file> [options]\tcheck if file is a valid TRX\n");
364 printf("\t-o offset\t\t\toffset of TRX data in file (default: 0)\n");
365 printf("\n");
366 printf("Extracting from TRX file:\n");
367 printf("\totrx extract <file> [options]\textract partitions from TRX file\n");
368 printf("\t-o offset\t\t\toffset of TRX data in file (default: 0)\n");
369 printf("\t-1 file\t\t\t\tfile to extract 1st partition to (optional)\n");
370 printf("\t-2 file\t\t\t\tfile to extract 2nd partition to (optional)\n");
371 printf("\t-3 file\t\t\t\tfile to extract 3rd partition to (optional)\n");
372 }
373
374 int main(int argc, char **argv) {
375 if (argc > 1) {
376 if (!strcmp(argv[1], "check"))
377 return otrx_check(argc, argv);
378 else if (!strcmp(argv[1], "extract"))
379 return otrx_extract(argc, argv);
380 }
381
382 usage();
383 return 0;
384 }