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