11db426a1df7036c14705f8b931dc9999eda401a
[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 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 }