otrx: support for creating simple TRX files
[openwrt/staging/yousong.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 }