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