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