otrx: make crc32 table global for further optimizations
[15.05/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 }