otrx: bump buffer size in otrx_create_append_file
[openwrt/staging/chunkeey.git] / package / utils / otrx / 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 return -EIO;
268 }
269
270 return length;
271 }
272
273 static ssize_t otrx_create_align(FILE *trx, size_t curr_offset, size_t alignment) {
274 if (curr_offset & (alignment - 1)) {
275 size_t length = alignment - (curr_offset % alignment);
276 return otrx_create_append_zeros(trx, length);
277 }
278
279 return 0;
280 }
281
282 static int otrx_create_write_hdr(FILE *trx, struct trx_header *hdr) {
283 size_t bytes, length;
284 uint8_t buf[1024];
285 uint32_t crc32;
286
287 hdr->magic = cpu_to_le32(TRX_MAGIC);
288 hdr->version = 1;
289
290 fseek(trx, 0, SEEK_SET);
291 bytes = fwrite(hdr, 1, sizeof(struct trx_header), trx);
292 if (bytes != sizeof(struct trx_header)) {
293 fprintf(stderr, "Couldn't write TRX header to %s\n", trx_path);
294 return -EIO;
295 }
296
297 length = le32_to_cpu(hdr->length);
298
299 crc32 = 0xffffffff;
300 fseek(trx, TRX_FLAGS_OFFSET, SEEK_SET);
301 length -= TRX_FLAGS_OFFSET;
302 while ((bytes = fread(buf, 1, otrx_min(sizeof(buf), length), trx)) > 0) {
303 crc32 = otrx_crc32(crc32, buf, bytes);
304 length -= bytes;
305 }
306 hdr->crc32 = cpu_to_le32(crc32);
307
308 fseek(trx, 0, SEEK_SET);
309 bytes = fwrite(hdr, 1, sizeof(struct trx_header), trx);
310 if (bytes != sizeof(struct trx_header)) {
311 fprintf(stderr, "Couldn't write TRX header to %s\n", trx_path);
312 return -EIO;
313 }
314
315 return 0;
316 }
317
318 static int otrx_create(int argc, char **argv) {
319 FILE *trx;
320 struct trx_header hdr = {};
321 ssize_t sbytes;
322 size_t curr_idx = 0;
323 size_t curr_offset = sizeof(hdr);
324 int c;
325 int err = 0;
326
327 if (argc < 3) {
328 fprintf(stderr, "No TRX file passed\n");
329 err = -EINVAL;
330 goto out;
331 }
332 trx_path = argv[2];
333
334 trx = fopen(trx_path, "w+");
335 if (!trx) {
336 fprintf(stderr, "Couldn't open %s\n", trx_path);
337 err = -EACCES;
338 goto out;
339 }
340 fseek(trx, curr_offset, SEEK_SET);
341
342 optind = 3;
343 while ((c = getopt(argc, argv, "f:A:a:b:")) != -1) {
344 switch (c) {
345 case 'f':
346 if (curr_idx >= TRX_MAX_PARTS) {
347 err = -ENOSPC;
348 fprintf(stderr, "Reached TRX partitions limit, no place for %s\n", optarg);
349 goto err_close;
350 }
351
352 sbytes = otrx_create_append_file(trx, optarg);
353 if (sbytes < 0) {
354 fprintf(stderr, "Failed to append file %s\n", optarg);
355 } else {
356 hdr.offset[curr_idx++] = curr_offset;
357 curr_offset += sbytes;
358 }
359
360 sbytes = otrx_create_align(trx, curr_offset, 4);
361 if (sbytes < 0)
362 fprintf(stderr, "Failed to append zeros\n");
363 else
364 curr_offset += sbytes;
365
366 break;
367 case 'A':
368 sbytes = otrx_create_append_file(trx, optarg);
369 if (sbytes < 0) {
370 fprintf(stderr, "Failed to append file %s\n", optarg);
371 } else {
372 curr_offset += sbytes;
373 }
374
375 sbytes = otrx_create_align(trx, curr_offset, 4);
376 if (sbytes < 0)
377 fprintf(stderr, "Failed to append zeros\n");
378 else
379 curr_offset += sbytes;
380 break;
381 case 'a':
382 sbytes = otrx_create_align(trx, curr_offset, strtol(optarg, NULL, 0));
383 if (sbytes < 0)
384 fprintf(stderr, "Failed to append zeros\n");
385 else
386 curr_offset += sbytes;
387 break;
388 case 'b':
389 sbytes = strtol(optarg, NULL, 0) - curr_offset;
390 if (sbytes < 0) {
391 fprintf(stderr, "Current TRX length is 0x%zx, can't pad it with zeros to 0x%lx\n", curr_offset, strtol(optarg, NULL, 0));
392 } else {
393 sbytes = otrx_create_append_zeros(trx, sbytes);
394 if (sbytes < 0)
395 fprintf(stderr, "Failed to append zeros\n");
396 else
397 curr_offset += sbytes;
398 }
399 break;
400 }
401 if (err)
402 break;
403 }
404
405 sbytes = otrx_create_align(trx, curr_offset, 0x1000);
406 if (sbytes < 0)
407 fprintf(stderr, "Failed to append zeros\n");
408 else
409 curr_offset += sbytes;
410
411 hdr.length = curr_offset;
412 otrx_create_write_hdr(trx, &hdr);
413 err_close:
414 fclose(trx);
415 out:
416 return err;
417 }
418
419 /**************************************************
420 * Extract
421 **************************************************/
422
423 static void otrx_extract_parse_options(int argc, char **argv) {
424 int c;
425
426 while ((c = getopt(argc, argv, "c:e:o:1:2:3:")) != -1) {
427 switch (c) {
428 case 'o':
429 trx_offset = atoi(optarg);
430 break;
431 case '1':
432 partition[0] = optarg;
433 break;
434 case '2':
435 partition[1] = optarg;
436 break;
437 case '3':
438 partition[2] = optarg;
439 break;
440 }
441 }
442 }
443
444 static int otrx_extract_copy(FILE *trx, size_t offset, size_t length, char *out_path) {
445 FILE *out;
446 size_t bytes;
447 uint8_t *buf;
448 int err = 0;
449
450 out = fopen(out_path, "w");
451 if (!out) {
452 fprintf(stderr, "Couldn't open %s\n", out_path);
453 err = -EACCES;
454 goto out;
455 }
456
457 buf = malloc(length);
458 if (!buf) {
459 fprintf(stderr, "Couldn't alloc %zu B buffer\n", length);
460 err = -ENOMEM;
461 goto err_close;
462 }
463
464 fseek(trx, offset, SEEK_SET);
465 bytes = fread(buf, 1, length, trx);
466 if (bytes != length) {
467 fprintf(stderr, "Couldn't read %zu B of data from %s\n", length, trx_path);
468 err = -ENOMEM;
469 goto err_free_buf;
470 };
471
472 bytes = fwrite(buf, 1, length, out);
473 if (bytes != length) {
474 fprintf(stderr, "Couldn't write %zu B to %s\n", length, out_path);
475 err = -ENOMEM;
476 goto err_free_buf;
477 }
478
479 printf("Extracted 0x%zx bytes into %s\n", length, out_path);
480
481 err_free_buf:
482 free(buf);
483 err_close:
484 fclose(out);
485 out:
486 return err;
487 }
488
489 static int otrx_extract(int argc, char **argv) {
490 FILE *trx;
491 struct trx_header hdr;
492 size_t bytes;
493 int i;
494 int err = 0;
495
496 if (argc < 3) {
497 fprintf(stderr, "No TRX file passed\n");
498 err = -EINVAL;
499 goto out;
500 }
501 trx_path = argv[2];
502
503 optind = 3;
504 otrx_extract_parse_options(argc, argv);
505
506 trx = fopen(trx_path, "r");
507 if (!trx) {
508 fprintf(stderr, "Couldn't open %s\n", trx_path);
509 err = -EACCES;
510 goto out;
511 }
512
513 fseek(trx, trx_offset, SEEK_SET);
514 bytes = fread(&hdr, 1, sizeof(hdr), trx);
515 if (bytes != sizeof(hdr)) {
516 fprintf(stderr, "Couldn't read %s header\n", trx_path);
517 err = -EIO;
518 goto err_close;
519 }
520
521 if (le32_to_cpu(hdr.magic) != TRX_MAGIC) {
522 fprintf(stderr, "Invalid TRX magic: 0x%08x\n", le32_to_cpu(hdr.magic));
523 err = -EINVAL;
524 goto err_close;
525 }
526
527 for (i = 0; i < TRX_MAX_PARTS; i++) {
528 size_t length;
529
530 if (!partition[i])
531 continue;
532 if (!hdr.offset[i]) {
533 printf("TRX doesn't contain partition %d, can't extract %s\n", i + 1, partition[i]);
534 continue;
535 }
536
537 if (i + 1 >= TRX_MAX_PARTS || !hdr.offset[i + 1])
538 length = le32_to_cpu(hdr.length) - le32_to_cpu(hdr.offset[i]);
539 else
540 length = le32_to_cpu(hdr.offset[i + 1]) - le32_to_cpu(hdr.offset[i]);
541
542 otrx_extract_copy(trx, trx_offset + le32_to_cpu(hdr.offset[i]), length, partition[i]);
543 }
544
545 err_close:
546 fclose(trx);
547 out:
548 return err;
549 }
550
551 /**************************************************
552 * Start
553 **************************************************/
554
555 static void usage() {
556 printf("Usage:\n");
557 printf("\n");
558 printf("Checking TRX file:\n");
559 printf("\totrx check <file> [options]\tcheck if file is a valid TRX\n");
560 printf("\t-o offset\t\t\toffset of TRX data in file (default: 0)\n");
561 printf("\n");
562 printf("Creating new TRX file:\n");
563 printf("\totrx create <file> [options] [partitions]\n");
564 printf("\t-f file\t\t\t\t[partition] start new partition with content copied from file\n");
565 printf("\t-A file\t\t\t\t[partition] append current partition with content copied from file\n");
566 printf("\t-a alignment\t\t\t[partition] align current partition\n");
567 printf("\t-b offset\t\t\t[partition] append zeros to partition till reaching absolute offset\n");
568 printf("\n");
569 printf("Extracting from TRX file:\n");
570 printf("\totrx extract <file> [options]\textract partitions from TRX file\n");
571 printf("\t-o offset\t\t\toffset of TRX data in file (default: 0)\n");
572 printf("\t-1 file\t\t\t\tfile to extract 1st partition to (optional)\n");
573 printf("\t-2 file\t\t\t\tfile to extract 2nd partition to (optional)\n");
574 printf("\t-3 file\t\t\t\tfile to extract 3rd partition to (optional)\n");
575 }
576
577 int main(int argc, char **argv) {
578 if (argc > 1) {
579 if (!strcmp(argv[1], "check"))
580 return otrx_check(argc, argv);
581 else if (!strcmp(argv[1], "create"))
582 return otrx_create(argc, argv);
583 else if (!strcmp(argv[1], "extract"))
584 return otrx_extract(argc, argv);
585 }
586
587 usage();
588 return 0;
589 }