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