base-files: don't set ARGV and ARGC
[openwrt/openwrt.git] / tools / firmware-utils / 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 ssize_t otrx_create_append_file(FILE *trx, const char *in_path) {
232 FILE *in;
233 size_t bytes;
234 ssize_t length = 0;
235 uint8_t buf[1024];
236
237 in = fopen(in_path, "r");
238 if (!in) {
239 fprintf(stderr, "Couldn't open %s\n", in_path);
240 return -EACCES;
241 }
242
243 while ((bytes = fread(buf, 1, sizeof(buf), in)) > 0) {
244 if (fwrite(buf, 1, bytes, trx) != bytes) {
245 fprintf(stderr, "Couldn't write %zu B to %s\n", bytes, trx_path);
246 length = -EIO;
247 break;
248 }
249 length += bytes;
250 }
251
252 fclose(in);
253
254 return length;
255 }
256
257 static ssize_t otrx_create_append_zeros(FILE *trx, size_t length) {
258 uint8_t *buf;
259
260 buf = malloc(length);
261 if (!buf)
262 return -ENOMEM;
263 memset(buf, 0, length);
264
265 if (fwrite(buf, 1, length, trx) != length) {
266 fprintf(stderr, "Couldn't write %zu B to %s\n", length, trx_path);
267 free(buf);
268 return -EIO;
269 }
270
271 free(buf);
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 trx = fopen(trx_path, "w+");
338 if (!trx) {
339 fprintf(stderr, "Couldn't open %s\n", trx_path);
340 err = -EACCES;
341 goto out;
342 }
343 fseek(trx, curr_offset, SEEK_SET);
344
345 optind = 3;
346 while ((c = getopt(argc, argv, "f:A:a:b:")) != -1) {
347 switch (c) {
348 case 'f':
349 if (curr_idx >= TRX_MAX_PARTS) {
350 err = -ENOSPC;
351 fprintf(stderr, "Reached TRX partitions limit, no place for %s\n", optarg);
352 goto err_close;
353 }
354
355 sbytes = otrx_create_append_file(trx, optarg);
356 if (sbytes < 0) {
357 fprintf(stderr, "Failed to append file %s\n", optarg);
358 } else {
359 hdr.offset[curr_idx++] = curr_offset;
360 curr_offset += sbytes;
361 }
362
363 sbytes = otrx_create_align(trx, curr_offset, 4);
364 if (sbytes < 0)
365 fprintf(stderr, "Failed to append zeros\n");
366 else
367 curr_offset += sbytes;
368
369 break;
370 case 'A':
371 sbytes = otrx_create_append_file(trx, optarg);
372 if (sbytes < 0) {
373 fprintf(stderr, "Failed to append file %s\n", optarg);
374 } else {
375 curr_offset += sbytes;
376 }
377
378 sbytes = otrx_create_align(trx, curr_offset, 4);
379 if (sbytes < 0)
380 fprintf(stderr, "Failed to append zeros\n");
381 else
382 curr_offset += sbytes;
383 break;
384 case 'a':
385 sbytes = otrx_create_align(trx, curr_offset, strtol(optarg, NULL, 0));
386 if (sbytes < 0)
387 fprintf(stderr, "Failed to append zeros\n");
388 else
389 curr_offset += sbytes;
390 break;
391 case 'b':
392 sbytes = strtol(optarg, NULL, 0) - curr_offset;
393 if (sbytes < 0) {
394 fprintf(stderr, "Current TRX length is 0x%zx, can't pad it with zeros to 0x%lx\n", curr_offset, strtol(optarg, NULL, 0));
395 } else {
396 sbytes = otrx_create_append_zeros(trx, sbytes);
397 if (sbytes < 0)
398 fprintf(stderr, "Failed to append zeros\n");
399 else
400 curr_offset += sbytes;
401 }
402 break;
403 }
404 if (err)
405 break;
406 }
407
408 sbytes = otrx_create_align(trx, curr_offset, 0x1000);
409 if (sbytes < 0)
410 fprintf(stderr, "Failed to append zeros\n");
411 else
412 curr_offset += sbytes;
413
414 hdr.length = curr_offset;
415 otrx_create_write_hdr(trx, &hdr);
416 err_close:
417 fclose(trx);
418 out:
419 return err;
420 }
421
422 /**************************************************
423 * Extract
424 **************************************************/
425
426 static void otrx_extract_parse_options(int argc, char **argv) {
427 int c;
428
429 while ((c = getopt(argc, argv, "c:e:o:1:2:3:")) != -1) {
430 switch (c) {
431 case 'o':
432 trx_offset = atoi(optarg);
433 break;
434 case '1':
435 partition[0] = optarg;
436 break;
437 case '2':
438 partition[1] = optarg;
439 break;
440 case '3':
441 partition[2] = optarg;
442 break;
443 }
444 }
445 }
446
447 static int otrx_extract_copy(FILE *trx, size_t offset, size_t length, char *out_path) {
448 FILE *out;
449 size_t bytes;
450 uint8_t *buf;
451 int err = 0;
452
453 out = fopen(out_path, "w");
454 if (!out) {
455 fprintf(stderr, "Couldn't open %s\n", out_path);
456 err = -EACCES;
457 goto out;
458 }
459
460 buf = malloc(length);
461 if (!buf) {
462 fprintf(stderr, "Couldn't alloc %zu B buffer\n", length);
463 err = -ENOMEM;
464 goto err_close;
465 }
466
467 fseek(trx, offset, SEEK_SET);
468 bytes = fread(buf, 1, length, trx);
469 if (bytes != length) {
470 fprintf(stderr, "Couldn't read %zu B of data from %s\n", length, trx_path);
471 err = -ENOMEM;
472 goto err_free_buf;
473 };
474
475 bytes = fwrite(buf, 1, length, out);
476 if (bytes != length) {
477 fprintf(stderr, "Couldn't write %zu B to %s\n", length, out_path);
478 err = -ENOMEM;
479 goto err_free_buf;
480 }
481
482 printf("Extracted 0x%zx bytes into %s\n", length, out_path);
483
484 err_free_buf:
485 free(buf);
486 err_close:
487 fclose(out);
488 out:
489 return err;
490 }
491
492 static int otrx_extract(int argc, char **argv) {
493 FILE *trx;
494 struct trx_header hdr;
495 size_t bytes;
496 int i;
497 int err = 0;
498
499 if (argc < 3) {
500 fprintf(stderr, "No TRX file passed\n");
501 err = -EINVAL;
502 goto out;
503 }
504 trx_path = argv[2];
505
506 optind = 3;
507 otrx_extract_parse_options(argc, argv);
508
509 trx = fopen(trx_path, "r");
510 if (!trx) {
511 fprintf(stderr, "Couldn't open %s\n", trx_path);
512 err = -EACCES;
513 goto out;
514 }
515
516 fseek(trx, trx_offset, SEEK_SET);
517 bytes = fread(&hdr, 1, sizeof(hdr), trx);
518 if (bytes != sizeof(hdr)) {
519 fprintf(stderr, "Couldn't read %s header\n", trx_path);
520 err = -EIO;
521 goto err_close;
522 }
523
524 if (le32_to_cpu(hdr.magic) != TRX_MAGIC) {
525 fprintf(stderr, "Invalid TRX magic: 0x%08x\n", le32_to_cpu(hdr.magic));
526 err = -EINVAL;
527 goto err_close;
528 }
529
530 for (i = 0; i < TRX_MAX_PARTS; i++) {
531 size_t length;
532
533 if (!partition[i])
534 continue;
535 if (!hdr.offset[i]) {
536 printf("TRX doesn't contain partition %d, can't extract %s\n", i + 1, partition[i]);
537 continue;
538 }
539
540 if (i + 1 >= TRX_MAX_PARTS || !hdr.offset[i + 1])
541 length = le32_to_cpu(hdr.length) - le32_to_cpu(hdr.offset[i]);
542 else
543 length = le32_to_cpu(hdr.offset[i + 1]) - le32_to_cpu(hdr.offset[i]);
544
545 otrx_extract_copy(trx, trx_offset + le32_to_cpu(hdr.offset[i]), length, partition[i]);
546 }
547
548 err_close:
549 fclose(trx);
550 out:
551 return err;
552 }
553
554 /**************************************************
555 * Start
556 **************************************************/
557
558 static void usage() {
559 printf("Usage:\n");
560 printf("\n");
561 printf("Checking TRX file:\n");
562 printf("\totrx check <file> [options]\tcheck if file is a valid TRX\n");
563 printf("\t-o offset\t\t\toffset of TRX data in file (default: 0)\n");
564 printf("\n");
565 printf("Creating new TRX file:\n");
566 printf("\totrx create <file> [options] [partitions]\n");
567 printf("\t-f file\t\t\t\t[partition] start new partition with content copied from file\n");
568 printf("\t-A file\t\t\t\t[partition] append current partition with content copied from file\n");
569 printf("\t-a alignment\t\t\t[partition] align current partition\n");
570 printf("\t-b offset\t\t\t[partition] append zeros to partition till reaching absolute offset\n");
571 printf("\n");
572 printf("Extracting from TRX file:\n");
573 printf("\totrx extract <file> [options]\textract partitions from TRX file\n");
574 printf("\t-o offset\t\t\toffset of TRX data in file (default: 0)\n");
575 printf("\t-1 file\t\t\t\tfile to extract 1st partition to (optional)\n");
576 printf("\t-2 file\t\t\t\tfile to extract 2nd partition to (optional)\n");
577 printf("\t-3 file\t\t\t\tfile to extract 3rd partition to (optional)\n");
578 }
579
580 int main(int argc, char **argv) {
581 if (argc > 1) {
582 if (!strcmp(argv[1], "check"))
583 return otrx_check(argc, argv);
584 else if (!strcmp(argv[1], "create"))
585 return otrx_create(argc, argv);
586 else if (!strcmp(argv[1], "extract"))
587 return otrx_extract(argc, argv);
588 }
589
590 usage();
591 return 0;
592 }