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