otrx: support for creating simple TRX files
[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  * Create
225  **************************************************/
226
227 static void otrx_create_parse_options(int argc, char **argv) {
228 }
229
230 static ssize_t otrx_create_append_file(FILE *trx, const char *in_path) {
231         FILE *in;
232         size_t bytes;
233         ssize_t length = 0;
234         uint8_t buf[128];
235
236         in = fopen(in_path, "r");
237         if (!in) {
238                 fprintf(stderr, "Couldn't open %s\n", in_path);
239                 return -EACCES;
240         }
241
242         while ((bytes = fread(buf, 1, sizeof(buf), in)) > 0) {
243                 if (fwrite(buf, 1, bytes, trx) != bytes) {
244                         fprintf(stderr, "Couldn't write %zu B to %s\n", bytes, trx_path);
245                         length = -EIO;
246                         break;
247                 }
248                 length += bytes;
249         }
250
251         fclose(in);
252
253         return length;
254 }
255
256 static ssize_t otrx_create_append_zeros(FILE *trx, size_t length) {
257         uint8_t *buf;
258
259         buf = malloc(length);
260         if (!buf)
261                 return -ENOMEM;
262         memset(buf, 0, length);
263
264         if (fwrite(buf, 1, length, trx) != length) {
265                 fprintf(stderr, "Couldn't write %zu B to %s\n", length, trx_path);
266                 return -EIO;
267         }
268
269         return length;
270 }
271
272 static ssize_t otrx_create_align(FILE *trx, size_t curr_offset, size_t alignment) {
273         if (curr_offset & (alignment - 1)) {
274                 size_t length = alignment - (curr_offset % alignment);
275                 return otrx_create_append_zeros(trx, length);
276         }
277
278         return 0;
279 }
280
281 static int otrx_create_write_hdr(FILE *trx, struct trx_header *hdr) {
282         size_t bytes, length;
283         uint8_t *buf;
284         uint32_t crc32;
285
286         hdr->magic = cpu_to_le32(TRX_MAGIC);
287         hdr->version = 1;
288
289         fseek(trx, 0, SEEK_SET);
290         bytes = fwrite(hdr, 1, sizeof(struct trx_header), trx);
291         if (bytes != sizeof(struct trx_header)) {
292                 fprintf(stderr, "Couldn't write TRX header to %s\n", trx_path);
293                 return -EIO;
294         }
295
296         length = le32_to_cpu(hdr->length);
297
298         buf = malloc(length);
299         if (!buf) {
300                 fprintf(stderr, "Couldn't alloc %zu B buffer\n", length);
301                 return -ENOMEM;
302         }
303
304         fseek(trx, 0, SEEK_SET);
305         bytes = fread(buf, 1, length, trx);
306         if (bytes != length) {
307                 fprintf(stderr, "Couldn't read %zu B of data from %s\n", length, trx_path);
308                 return -ENOMEM;
309         }
310
311         crc32 = otrx_crc32(buf + TRX_FLAGS_OFFSET, length - TRX_FLAGS_OFFSET);
312         hdr->crc32 = cpu_to_le32(crc32);
313
314         fseek(trx, 0, SEEK_SET);
315         bytes = fwrite(hdr, 1, sizeof(struct trx_header), trx);
316         if (bytes != sizeof(struct trx_header)) {
317                 fprintf(stderr, "Couldn't write TRX header to %s\n", trx_path);
318                 return -EIO;
319         }
320
321         return 0;
322 }
323
324 static int otrx_create(int argc, char **argv) {
325         FILE *trx;
326         struct trx_header hdr = {};
327         ssize_t sbytes;
328         size_t curr_idx = 0;
329         size_t curr_offset = sizeof(hdr);
330         int c;
331         int err = 0;
332
333         if (argc < 3) {
334                 fprintf(stderr, "No TRX file passed\n");
335                 err = -EINVAL;
336                 goto out;
337         }
338         trx_path = argv[2];
339
340         optind = 3;
341         otrx_create_parse_options(argc, argv);
342
343         trx = fopen(trx_path, "w+");
344         if (!trx) {
345                 fprintf(stderr, "Couldn't open %s\n", trx_path);
346                 err = -EACCES;
347                 goto out;
348         }
349         fseek(trx, curr_offset, SEEK_SET);
350
351         optind = 3;
352         while ((c = getopt(argc, argv, "f:b:")) != -1) {
353                 switch (c) {
354                 case 'f':
355                         if (curr_idx >= TRX_MAX_PARTS) {
356                                 err = -ENOSPC;
357                                 fprintf(stderr, "Reached TRX partitions limit, no place for %s\n", optarg);
358                                 goto err_close;
359                         }
360
361                         sbytes = otrx_create_append_file(trx, optarg);
362                         if (sbytes < 0) {
363                                 fprintf(stderr, "Failed to append file %s\n", optarg);
364                         } else {
365                                 hdr.offset[curr_idx++] = curr_offset;
366                                 curr_offset += sbytes;
367                         }
368
369                         sbytes = otrx_create_align(trx, curr_offset, 4);
370                         if (sbytes < 0)
371                                 fprintf(stderr, "Failed to append zeros\n");
372                         else
373                                 curr_offset += sbytes;
374
375                         break;
376                 case 'b':
377                         sbytes = strtol(optarg, NULL, 0) - curr_offset;
378                         if (sbytes < 0) {
379                                 fprintf(stderr, "Current TRX length is 0x%zx, can't pad it with zeros to 0x%lx\n", curr_offset, strtol(optarg, NULL, 0));
380                         } else {
381                                 sbytes = otrx_create_append_zeros(trx, sbytes);
382                                 if (sbytes < 0)
383                                         fprintf(stderr, "Failed to append zeros\n");
384                                 else
385                                         curr_offset += sbytes;
386                         }
387                         break;
388                 }
389                 if (err)
390                         break;
391         }
392
393         hdr.length = curr_offset;
394         otrx_create_write_hdr(trx, &hdr);
395 err_close:
396         fclose(trx);
397 out:
398         return err;
399 }
400
401 /**************************************************
402  * Extract
403  **************************************************/
404
405 static void otrx_extract_parse_options(int argc, char **argv) {
406         int c;
407
408         while ((c = getopt(argc, argv, "c:e:o:1:2:3:")) != -1) {
409                 switch (c) {
410                 case 'o':
411                         trx_offset = atoi(optarg);
412                         break;
413                 case '1':
414                         partition[0] = optarg;
415                         break;
416                 case '2':
417                         partition[1] = optarg;
418                         break;
419                 case '3':
420                         partition[2] = optarg;
421                         break;
422                 }
423         }
424 }
425
426 static int otrx_extract_copy(FILE *trx, size_t offset, size_t length, char *out_path) {
427         FILE *out;
428         size_t bytes;
429         uint8_t *buf;
430         int err = 0;
431
432         out = fopen(out_path, "w");
433         if (!out) {
434                 fprintf(stderr, "Couldn't open %s\n", out_path);
435                 err = -EACCES;
436                 goto out;
437         }
438
439         buf = malloc(length);
440         if (!buf) {
441                 fprintf(stderr, "Couldn't alloc %zu B buffer\n", length);
442                 err =  -ENOMEM;
443                 goto err_close;
444         }
445
446         fseek(trx, offset, SEEK_SET);
447         bytes = fread(buf, 1, length, trx);
448         if (bytes != length) {
449                 fprintf(stderr, "Couldn't read %zu B of data from %s\n", length, trx_path);
450                 err =  -ENOMEM;
451                 goto err_free_buf;
452         };
453
454         bytes = fwrite(buf, 1, length, out);
455         if (bytes != length) {
456                 fprintf(stderr, "Couldn't write %zu B to %s\n", length, out_path);
457                 err =  -ENOMEM;
458                 goto err_free_buf;
459         }
460
461         printf("Extracted 0x%zx bytes into %s\n", length, out_path);
462
463 err_free_buf:
464         free(buf);
465 err_close:
466         fclose(out);
467 out:
468         return err;
469 }
470
471 static int otrx_extract(int argc, char **argv) {
472         FILE *trx;
473         struct trx_header hdr;
474         size_t bytes;
475         int i;
476         int err = 0;
477
478         if (argc < 3) {
479                 fprintf(stderr, "No TRX file passed\n");
480                 err = -EINVAL;
481                 goto out;
482         }
483         trx_path = argv[2];
484
485         optind = 3;
486         otrx_extract_parse_options(argc, argv);
487
488         trx = fopen(trx_path, "r");
489         if (!trx) {
490                 fprintf(stderr, "Couldn't open %s\n", trx_path);
491                 err = -EACCES;
492                 goto out;
493         }
494
495         fseek(trx, trx_offset, SEEK_SET);
496         bytes = fread(&hdr, 1, sizeof(hdr), trx);
497         if (bytes != sizeof(hdr)) {
498                 fprintf(stderr, "Couldn't read %s header\n", trx_path);
499                 err =  -EIO;
500                 goto err_close;
501         }
502
503         if (le32_to_cpu(hdr.magic) != TRX_MAGIC) {
504                 fprintf(stderr, "Invalid TRX magic: 0x%08x\n", le32_to_cpu(hdr.magic));
505                 err =  -EINVAL;
506                 goto err_close;
507         }
508
509         for (i = 0; i < TRX_MAX_PARTS; i++) {
510                 size_t length;
511
512                 if (!partition[i])
513                         continue;
514                 if (!hdr.offset[i]) {
515                         printf("TRX doesn't contain partition %d, can't extract %s\n", i + 1, partition[i]);
516                         continue;
517                 }
518
519                 if (i + 1 >= TRX_MAX_PARTS || !hdr.offset[i + 1])
520                         length = le32_to_cpu(hdr.length) - le32_to_cpu(hdr.offset[i]);
521                 else
522                         length = le32_to_cpu(hdr.offset[i + 1]) - le32_to_cpu(hdr.offset[i]);
523
524                 otrx_extract_copy(trx, trx_offset + le32_to_cpu(hdr.offset[i]), length, partition[i]);
525         }
526
527 err_close:
528         fclose(trx);
529 out:
530         return err;
531 }
532
533 /**************************************************
534  * Start
535  **************************************************/
536
537 static void usage() {
538         printf("Usage:\n");
539         printf("\n");
540         printf("Checking TRX file:\n");
541         printf("\totrx check <file> [options]\tcheck if file is a valid TRX\n");
542         printf("\t-o offset\t\t\toffset of TRX data in file (default: 0)\n");
543         printf("\n");
544         printf("Creating new TRX file:\n");
545         printf("\totrx create <file> [options] [partitions]\n");
546         printf("\t-f file\t\t\t\t[partition] start new partition with content copied from file\n");
547         printf("\t-b offset\t\t\t[partition] append zeros to partition till reaching absolute offset\n");
548         printf("\n");
549         printf("Extracting from TRX file:\n");
550         printf("\totrx extract <file> [options]\textract partitions from TRX file\n");
551         printf("\t-o offset\t\t\toffset of TRX data in file (default: 0)\n");
552         printf("\t-1 file\t\t\t\tfile to extract 1st partition to (optional)\n");
553         printf("\t-2 file\t\t\t\tfile to extract 2nd partition to (optional)\n");
554         printf("\t-3 file\t\t\t\tfile to extract 3rd partition to (optional)\n");
555 }
556
557 int main(int argc, char **argv) {
558         if (argc > 1) {
559                 if (!strcmp(argv[1], "check"))
560                         return otrx_check(argc, argv);
561                 else if (!strcmp(argv[1], "create"))
562                         return otrx_create(argc, argv);
563                 else if (!strcmp(argv[1], "extract"))
564                         return otrx_extract(argc, argv);
565         }
566
567         usage();
568         return 0;
569 }