otrx: bump buffer size in otrx_create_append_file
[oweals/openwrt.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 }