otrx: make crc32 table global for further optimizations
[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 static const uint32_t crc32_tbl[] = {
52 0x00000000, 0x77073096, 0xee0e612c, 0x990951ba,
53 0x076dc419, 0x706af48f, 0xe963a535, 0x9e6495a3,
54 0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988,
55 0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91,
56 0x1db71064, 0x6ab020f2, 0xf3b97148, 0x84be41de,
57 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7,
58 0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec,
59 0x14015c4f, 0x63066cd9, 0xfa0f3d63, 0x8d080df5,
60 0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172,
61 0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b,
62 0x35b5a8fa, 0x42b2986c, 0xdbbbc9d6, 0xacbcf940,
63 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59,
64 0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116,
65 0x21b4f4b5, 0x56b3c423, 0xcfba9599, 0xb8bda50f,
66 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924,
67 0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d,
68 0x76dc4190, 0x01db7106, 0x98d220bc, 0xefd5102a,
69 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433,
70 0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818,
71 0x7f6a0dbb, 0x086d3d2d, 0x91646c97, 0xe6635c01,
72 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e,
73 0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457,
74 0x65b0d9c6, 0x12b7e950, 0x8bbeb8ea, 0xfcb9887c,
75 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65,
76 0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2,
77 0x4adfa541, 0x3dd895d7, 0xa4d1c46d, 0xd3d6f4fb,
78 0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0,
79 0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9,
80 0x5005713c, 0x270241aa, 0xbe0b1010, 0xc90c2086,
81 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f,
82 0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4,
83 0x59b33d17, 0x2eb40d81, 0xb7bd5c3b, 0xc0ba6cad,
84 0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a,
85 0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683,
86 0xe3630b12, 0x94643b84, 0x0d6d6a3e, 0x7a6a5aa8,
87 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1,
88 0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe,
89 0xf762575d, 0x806567cb, 0x196c3671, 0x6e6b06e7,
90 0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc,
91 0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5,
92 0xd6d6a3e8, 0xa1d1937e, 0x38d8c2c4, 0x4fdff252,
93 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b,
94 0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60,
95 0xdf60efc3, 0xa867df55, 0x316e8eef, 0x4669be79,
96 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236,
97 0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f,
98 0xc5ba3bbe, 0xb2bd0b28, 0x2bb45a92, 0x5cb36a04,
99 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d,
100 0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a,
101 0x9c0906a9, 0xeb0e363f, 0x72076785, 0x05005713,
102 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38,
103 0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21,
104 0x86d3d2d4, 0xf1d4e242, 0x68ddb3f8, 0x1fda836e,
105 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777,
106 0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c,
107 0x8f659eff, 0xf862ae69, 0x616bffd3, 0x166ccf45,
108 0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2,
109 0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db,
110 0xaed16a4a, 0xd9d65adc, 0x40df0b66, 0x37d83bf0,
111 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9,
112 0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6,
113 0xbad03605, 0xcdd70693, 0x54de5729, 0x23d967bf,
114 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94,
115 0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d,
116 };
117
118 uint32_t otrx_crc32(uint8_t *buf, size_t len) {
119 uint32_t crc = 0xffffffff;
120
121 while (len) {
122 crc = crc32_tbl[(crc ^ *buf) & 0xff] ^ (crc >> 8);
123 buf++;
124 len--;
125 }
126
127 return crc;
128 }
129
130 /**************************************************
131 * Check
132 **************************************************/
133
134 static void otrx_check_parse_options(int argc, char **argv) {
135 int c;
136
137 while ((c = getopt(argc, argv, "o:")) != -1) {
138 switch (c) {
139 case 'o':
140 trx_offset = atoi(optarg);
141 break;
142 }
143 }
144 }
145
146 static int otrx_check(int argc, char **argv) {
147 FILE *trx;
148 struct trx_header hdr;
149 size_t bytes, length;
150 uint8_t *buf;
151 uint32_t crc32;
152 int err = 0;
153
154 if (argc < 3) {
155 fprintf(stderr, "No TRX file passed\n");
156 err = -EINVAL;
157 goto out;
158 }
159 trx_path = argv[2];
160
161 optind = 3;
162 otrx_check_parse_options(argc, argv);
163
164 trx = fopen(trx_path, "r");
165 if (!trx) {
166 fprintf(stderr, "Couldn't open %s\n", trx_path);
167 err = -EACCES;
168 goto out;
169 }
170
171 fseek(trx, trx_offset, SEEK_SET);
172 bytes = fread(&hdr, 1, sizeof(hdr), trx);
173 if (bytes != sizeof(hdr)) {
174 fprintf(stderr, "Couldn't read %s header\n", trx_path);
175 err = -EIO;
176 goto err_close;
177 }
178
179 if (le32_to_cpu(hdr.magic) != TRX_MAGIC) {
180 fprintf(stderr, "Invalid TRX magic: 0x%08x\n", le32_to_cpu(hdr.magic));
181 err = -EINVAL;
182 goto err_close;
183 }
184
185 length = le32_to_cpu(hdr.length);
186 if (length < sizeof(hdr)) {
187 fprintf(stderr, "Length read from TRX too low (%zu B)\n", length);
188 err = -EINVAL;
189 goto err_close;
190 }
191
192 buf = malloc(length);
193 if (!buf) {
194 fprintf(stderr, "Couldn't alloc %zd B buffer\n", length);
195 err = -ENOMEM;
196 goto err_close;
197 }
198
199 fseek(trx, trx_offset, SEEK_SET);
200 bytes = fread(buf, 1, length, trx);
201 if (bytes != length) {
202 fprintf(stderr, "Couldn't read %zd B of data from %s\n", length, trx_path);
203 err = -ENOMEM;
204 goto err_free_buf;
205 }
206
207 crc32 = otrx_crc32(buf + TRX_FLAGS_OFFSET, length - TRX_FLAGS_OFFSET);
208 if (crc32 != le32_to_cpu(hdr.crc32)) {
209 fprintf(stderr, "Invalid data crc32: 0x%08x instead of 0x%08x\n", crc32, le32_to_cpu(hdr.crc32));
210 err = -EINVAL;
211 goto err_free_buf;
212 }
213
214 printf("Found a valid TRX version %d\n", le32_to_cpu(hdr.version));
215
216 err_free_buf:
217 free(buf);
218 err_close:
219 fclose(trx);
220 out:
221 return err;
222 }
223
224 /**************************************************
225 * Create
226 **************************************************/
227
228 static void otrx_create_parse_options(int argc, char **argv) {
229 }
230
231 static ssize_t otrx_create_append_file(FILE *trx, const char *in_path) {
232 FILE *in;
233 size_t bytes;
234 ssize_t length = 0;
235 uint8_t buf[128];
236
237 in = fopen(in_path, "r");
238 if (!in) {
239 fprintf(stderr, "Couldn't open %s\n", in_path);
240 return -EACCES;
241 }
242
243 while ((bytes = fread(buf, 1, sizeof(buf), in)) > 0) {
244 if (fwrite(buf, 1, bytes, trx) != bytes) {
245 fprintf(stderr, "Couldn't write %zu B to %s\n", bytes, trx_path);
246 length = -EIO;
247 break;
248 }
249 length += bytes;
250 }
251
252 fclose(in);
253
254 return length;
255 }
256
257 static ssize_t otrx_create_append_zeros(FILE *trx, size_t length) {
258 uint8_t *buf;
259
260 buf = malloc(length);
261 if (!buf)
262 return -ENOMEM;
263 memset(buf, 0, length);
264
265 if (fwrite(buf, 1, length, trx) != length) {
266 fprintf(stderr, "Couldn't write %zu B to %s\n", length, trx_path);
267 return -EIO;
268 }
269
270 return length;
271 }
272
273 static ssize_t otrx_create_align(FILE *trx, size_t curr_offset, size_t alignment) {
274 if (curr_offset & (alignment - 1)) {
275 size_t length = alignment - (curr_offset % alignment);
276 return otrx_create_append_zeros(trx, length);
277 }
278
279 return 0;
280 }
281
282 static int otrx_create_write_hdr(FILE *trx, struct trx_header *hdr) {
283 size_t bytes, length;
284 uint8_t *buf;
285 uint32_t crc32;
286
287 hdr->magic = cpu_to_le32(TRX_MAGIC);
288 hdr->version = 1;
289
290 fseek(trx, 0, SEEK_SET);
291 bytes = fwrite(hdr, 1, sizeof(struct trx_header), trx);
292 if (bytes != sizeof(struct trx_header)) {
293 fprintf(stderr, "Couldn't write TRX header to %s\n", trx_path);
294 return -EIO;
295 }
296
297 length = le32_to_cpu(hdr->length);
298
299 buf = malloc(length);
300 if (!buf) {
301 fprintf(stderr, "Couldn't alloc %zu B buffer\n", length);
302 return -ENOMEM;
303 }
304
305 fseek(trx, 0, SEEK_SET);
306 bytes = fread(buf, 1, length, trx);
307 if (bytes != length) {
308 fprintf(stderr, "Couldn't read %zu B of data from %s\n", length, trx_path);
309 return -ENOMEM;
310 }
311
312 crc32 = otrx_crc32(buf + TRX_FLAGS_OFFSET, length - TRX_FLAGS_OFFSET);
313 hdr->crc32 = cpu_to_le32(crc32);
314
315 fseek(trx, 0, SEEK_SET);
316 bytes = fwrite(hdr, 1, sizeof(struct trx_header), trx);
317 if (bytes != sizeof(struct trx_header)) {
318 fprintf(stderr, "Couldn't write TRX header to %s\n", trx_path);
319 return -EIO;
320 }
321
322 return 0;
323 }
324
325 static int otrx_create(int argc, char **argv) {
326 FILE *trx;
327 struct trx_header hdr = {};
328 ssize_t sbytes;
329 size_t curr_idx = 0;
330 size_t curr_offset = sizeof(hdr);
331 int c;
332 int err = 0;
333
334 if (argc < 3) {
335 fprintf(stderr, "No TRX file passed\n");
336 err = -EINVAL;
337 goto out;
338 }
339 trx_path = argv[2];
340
341 optind = 3;
342 otrx_create_parse_options(argc, argv);
343
344 trx = fopen(trx_path, "w+");
345 if (!trx) {
346 fprintf(stderr, "Couldn't open %s\n", trx_path);
347 err = -EACCES;
348 goto out;
349 }
350 fseek(trx, curr_offset, SEEK_SET);
351
352 optind = 3;
353 while ((c = getopt(argc, argv, "f:b:")) != -1) {
354 switch (c) {
355 case 'f':
356 if (curr_idx >= TRX_MAX_PARTS) {
357 err = -ENOSPC;
358 fprintf(stderr, "Reached TRX partitions limit, no place for %s\n", optarg);
359 goto err_close;
360 }
361
362 sbytes = otrx_create_append_file(trx, optarg);
363 if (sbytes < 0) {
364 fprintf(stderr, "Failed to append file %s\n", optarg);
365 } else {
366 hdr.offset[curr_idx++] = curr_offset;
367 curr_offset += sbytes;
368 }
369
370 sbytes = otrx_create_align(trx, curr_offset, 4);
371 if (sbytes < 0)
372 fprintf(stderr, "Failed to append zeros\n");
373 else
374 curr_offset += sbytes;
375
376 break;
377 case 'b':
378 sbytes = strtol(optarg, NULL, 0) - curr_offset;
379 if (sbytes < 0) {
380 fprintf(stderr, "Current TRX length is 0x%zx, can't pad it with zeros to 0x%lx\n", curr_offset, strtol(optarg, NULL, 0));
381 } else {
382 sbytes = otrx_create_append_zeros(trx, sbytes);
383 if (sbytes < 0)
384 fprintf(stderr, "Failed to append zeros\n");
385 else
386 curr_offset += sbytes;
387 }
388 break;
389 }
390 if (err)
391 break;
392 }
393
394 hdr.length = curr_offset;
395 otrx_create_write_hdr(trx, &hdr);
396 err_close:
397 fclose(trx);
398 out:
399 return err;
400 }
401
402 /**************************************************
403 * Extract
404 **************************************************/
405
406 static void otrx_extract_parse_options(int argc, char **argv) {
407 int c;
408
409 while ((c = getopt(argc, argv, "c:e:o:1:2:3:")) != -1) {
410 switch (c) {
411 case 'o':
412 trx_offset = atoi(optarg);
413 break;
414 case '1':
415 partition[0] = optarg;
416 break;
417 case '2':
418 partition[1] = optarg;
419 break;
420 case '3':
421 partition[2] = optarg;
422 break;
423 }
424 }
425 }
426
427 static int otrx_extract_copy(FILE *trx, size_t offset, size_t length, char *out_path) {
428 FILE *out;
429 size_t bytes;
430 uint8_t *buf;
431 int err = 0;
432
433 out = fopen(out_path, "w");
434 if (!out) {
435 fprintf(stderr, "Couldn't open %s\n", out_path);
436 err = -EACCES;
437 goto out;
438 }
439
440 buf = malloc(length);
441 if (!buf) {
442 fprintf(stderr, "Couldn't alloc %zu B buffer\n", length);
443 err = -ENOMEM;
444 goto err_close;
445 }
446
447 fseek(trx, offset, SEEK_SET);
448 bytes = fread(buf, 1, length, trx);
449 if (bytes != length) {
450 fprintf(stderr, "Couldn't read %zu B of data from %s\n", length, trx_path);
451 err = -ENOMEM;
452 goto err_free_buf;
453 };
454
455 bytes = fwrite(buf, 1, length, out);
456 if (bytes != length) {
457 fprintf(stderr, "Couldn't write %zu B to %s\n", length, out_path);
458 err = -ENOMEM;
459 goto err_free_buf;
460 }
461
462 printf("Extracted 0x%zx bytes into %s\n", length, out_path);
463
464 err_free_buf:
465 free(buf);
466 err_close:
467 fclose(out);
468 out:
469 return err;
470 }
471
472 static int otrx_extract(int argc, char **argv) {
473 FILE *trx;
474 struct trx_header hdr;
475 size_t bytes;
476 int i;
477 int err = 0;
478
479 if (argc < 3) {
480 fprintf(stderr, "No TRX file passed\n");
481 err = -EINVAL;
482 goto out;
483 }
484 trx_path = argv[2];
485
486 optind = 3;
487 otrx_extract_parse_options(argc, argv);
488
489 trx = fopen(trx_path, "r");
490 if (!trx) {
491 fprintf(stderr, "Couldn't open %s\n", trx_path);
492 err = -EACCES;
493 goto out;
494 }
495
496 fseek(trx, trx_offset, SEEK_SET);
497 bytes = fread(&hdr, 1, sizeof(hdr), trx);
498 if (bytes != sizeof(hdr)) {
499 fprintf(stderr, "Couldn't read %s header\n", trx_path);
500 err = -EIO;
501 goto err_close;
502 }
503
504 if (le32_to_cpu(hdr.magic) != TRX_MAGIC) {
505 fprintf(stderr, "Invalid TRX magic: 0x%08x\n", le32_to_cpu(hdr.magic));
506 err = -EINVAL;
507 goto err_close;
508 }
509
510 for (i = 0; i < TRX_MAX_PARTS; i++) {
511 size_t length;
512
513 if (!partition[i])
514 continue;
515 if (!hdr.offset[i]) {
516 printf("TRX doesn't contain partition %d, can't extract %s\n", i + 1, partition[i]);
517 continue;
518 }
519
520 if (i + 1 >= TRX_MAX_PARTS || !hdr.offset[i + 1])
521 length = le32_to_cpu(hdr.length) - le32_to_cpu(hdr.offset[i]);
522 else
523 length = le32_to_cpu(hdr.offset[i + 1]) - le32_to_cpu(hdr.offset[i]);
524
525 otrx_extract_copy(trx, trx_offset + le32_to_cpu(hdr.offset[i]), length, partition[i]);
526 }
527
528 err_close:
529 fclose(trx);
530 out:
531 return err;
532 }
533
534 /**************************************************
535 * Start
536 **************************************************/
537
538 static void usage() {
539 printf("Usage:\n");
540 printf("\n");
541 printf("Checking TRX file:\n");
542 printf("\totrx check <file> [options]\tcheck if file is a valid TRX\n");
543 printf("\t-o offset\t\t\toffset of TRX data in file (default: 0)\n");
544 printf("\n");
545 printf("Creating new TRX file:\n");
546 printf("\totrx create <file> [options] [partitions]\n");
547 printf("\t-f file\t\t\t\t[partition] start new partition with content copied from file\n");
548 printf("\t-b offset\t\t\t[partition] append zeros to partition till reaching absolute offset\n");
549 printf("\n");
550 printf("Extracting from TRX file:\n");
551 printf("\totrx extract <file> [options]\textract partitions from TRX file\n");
552 printf("\t-o offset\t\t\toffset of TRX data in file (default: 0)\n");
553 printf("\t-1 file\t\t\t\tfile to extract 1st partition to (optional)\n");
554 printf("\t-2 file\t\t\t\tfile to extract 2nd partition to (optional)\n");
555 printf("\t-3 file\t\t\t\tfile to extract 3rd partition to (optional)\n");
556 }
557
558 int main(int argc, char **argv) {
559 if (argc > 1) {
560 if (!strcmp(argv[1], "check"))
561 return otrx_check(argc, argv);
562 else if (!strcmp(argv[1], "create"))
563 return otrx_create(argc, argv);
564 else if (!strcmp(argv[1], "extract"))
565 return otrx_extract(argc, argv);
566 }
567
568 usage();
569 return 0;
570 }