e42aacaf0e430b04350ac253e29695f9c15068f5
[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 <errno.h>
14 #include <stdint.h>
15 #include <stdio.h>
16 #include <stdlib.h>
17 #include <unistd.h>
18
19 #if __BYTE_ORDER == __BIG_ENDIAN
20 #define cpu_to_le32(x)  bswap_32(x)
21 #define le32_to_cpu(x)  bswap_32(x)
22 #elif __BYTE_ORDER == __LITTLE_ENDIAN
23 #define cpu_to_le32(x)  (x)
24 #define le32_to_cpu(x)  (x)
25 #else
26 #error "Unsupported endianness"
27 #endif
28
29 #define TRX_MAGIC                       0x30524448
30 #define TRX_FLAGS_OFFSET                12
31 #define TRX_MAX_PARTS                   3
32
33 struct trx_header {
34         uint32_t magic;
35         uint32_t length;
36         uint32_t crc32;
37         uint16_t flags;
38         uint16_t version;
39         uint32_t offset[3];
40 };
41
42 enum mode {
43         MODE_UNKNOWN,
44         MODE_CHECK,
45         MODE_EXTRACT,
46 };
47
48 enum mode mode = MODE_UNKNOWN;
49
50 char *trx_path;
51 size_t trx_offset = 0;
52 char *partition[TRX_MAX_PARTS] = {};
53
54 /**************************************************
55  * CRC32
56  **************************************************/
57
58 uint32_t otrx_crc32(uint8_t *buf, size_t len) {
59         static const uint32_t t[] = {
60                 0x00000000, 0x77073096, 0xee0e612c, 0x990951ba,
61                 0x076dc419, 0x706af48f, 0xe963a535, 0x9e6495a3,
62                 0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988,
63                 0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91,
64                 0x1db71064, 0x6ab020f2, 0xf3b97148, 0x84be41de,
65                 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7,
66                 0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec,
67                 0x14015c4f, 0x63066cd9, 0xfa0f3d63, 0x8d080df5,
68                 0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172,
69                 0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b,
70                 0x35b5a8fa, 0x42b2986c, 0xdbbbc9d6, 0xacbcf940,
71                 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59,
72                 0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116,
73                 0x21b4f4b5, 0x56b3c423, 0xcfba9599, 0xb8bda50f,
74                 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924,
75                 0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d,
76                 0x76dc4190, 0x01db7106, 0x98d220bc, 0xefd5102a,
77                 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433,
78                 0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818,
79                 0x7f6a0dbb, 0x086d3d2d, 0x91646c97, 0xe6635c01,
80                 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e,
81                 0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457,
82                 0x65b0d9c6, 0x12b7e950, 0x8bbeb8ea, 0xfcb9887c,
83                 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65,
84                 0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2,
85                 0x4adfa541, 0x3dd895d7, 0xa4d1c46d, 0xd3d6f4fb,
86                 0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0,
87                 0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9,
88                 0x5005713c, 0x270241aa, 0xbe0b1010, 0xc90c2086,
89                 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f,
90                 0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4,
91                 0x59b33d17, 0x2eb40d81, 0xb7bd5c3b, 0xc0ba6cad,
92                 0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a,
93                 0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683,
94                 0xe3630b12, 0x94643b84, 0x0d6d6a3e, 0x7a6a5aa8,
95                 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1,
96                 0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe,
97                 0xf762575d, 0x806567cb, 0x196c3671, 0x6e6b06e7,
98                 0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc,
99                 0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5,
100                 0xd6d6a3e8, 0xa1d1937e, 0x38d8c2c4, 0x4fdff252,
101                 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b,
102                 0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60,
103                 0xdf60efc3, 0xa867df55, 0x316e8eef, 0x4669be79,
104                 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236,
105                 0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f,
106                 0xc5ba3bbe, 0xb2bd0b28, 0x2bb45a92, 0x5cb36a04,
107                 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d,
108                 0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a,
109                 0x9c0906a9, 0xeb0e363f, 0x72076785, 0x05005713,
110                 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38,
111                 0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21,
112                 0x86d3d2d4, 0xf1d4e242, 0x68ddb3f8, 0x1fda836e,
113                 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777,
114                 0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c,
115                 0x8f659eff, 0xf862ae69, 0x616bffd3, 0x166ccf45,
116                 0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2,
117                 0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db,
118                 0xaed16a4a, 0xd9d65adc, 0x40df0b66, 0x37d83bf0,
119                 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9,
120                 0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6,
121                 0xbad03605, 0xcdd70693, 0x54de5729, 0x23d967bf,
122                 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94,
123                 0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d,
124         };
125         uint32_t crc = 0xffffffff;
126
127         while (len) {
128                 crc = t[(crc ^ *buf) & 0xff] ^ (crc >> 8);
129                 buf++;
130                 len--;
131         }
132
133         return crc;
134 }
135
136 /**************************************************
137  * Check
138  **************************************************/
139
140 static int otrx_check() {
141         FILE *trx;
142         struct trx_header hdr;
143         size_t bytes, length;
144         uint8_t *buf;
145         uint32_t crc32;
146         int err = 0;
147
148         trx = fopen(trx_path, "r");
149         if (!trx) {
150                 fprintf(stderr, "Couldn't open %s\n", trx_path);
151                 err = -EACCES;
152                 goto out;
153         }
154
155         fseek(trx, trx_offset, SEEK_SET);
156         bytes = fread(&hdr, 1, sizeof(hdr), trx);
157         if (bytes != sizeof(hdr)) {
158                 fprintf(stderr, "Couldn't read %s header\n", trx_path);
159                 err =  -EIO;
160                 goto err_close;
161         }
162
163         if (le32_to_cpu(hdr.magic) != TRX_MAGIC) {
164                 fprintf(stderr, "Invalid TRX magic: 0x%08x\n", le32_to_cpu(hdr.magic));
165                 err =  -EINVAL;
166                 goto err_close;
167         }
168
169         length = le32_to_cpu(hdr.length);
170         if (length < sizeof(hdr)) {
171                 fprintf(stderr, "Length read from TRX too low (%zu B)\n", length);
172                 err = -EINVAL;
173                 goto err_close;
174         }
175
176         buf = malloc(length);
177         if (!buf) {
178                 fprintf(stderr, "Couldn't alloc %zd B buffer\n", length);
179                 err =  -ENOMEM;
180                 goto err_close;
181         }
182
183         fseek(trx, trx_offset, SEEK_SET);
184         bytes = fread(buf, 1, length, trx);
185         if (bytes != length) {
186                 fprintf(stderr, "Couldn't read %zd B of data from %s\n", length, trx_path);
187                 err =  -ENOMEM;
188                 goto err_free_buf;
189         }
190
191         crc32 = otrx_crc32(buf + TRX_FLAGS_OFFSET, length - TRX_FLAGS_OFFSET);
192         if (crc32 != le32_to_cpu(hdr.crc32)) {
193                 fprintf(stderr, "Invalid data crc32: 0x%08x instead of 0x%08x\n", crc32, le32_to_cpu(hdr.crc32));
194                 err =  -EINVAL;
195                 goto err_free_buf;
196         }
197
198         printf("Found a valid TRX version %d\n", le32_to_cpu(hdr.version));
199
200 err_free_buf:
201         free(buf);
202 err_close:
203         fclose(trx);
204 out:
205         return err;
206 }
207
208 /**************************************************
209  * Extract
210  **************************************************/
211
212 static int otrx_extract_copy(FILE *trx, size_t offset, size_t length, char *out_path) {
213         FILE *out;
214         size_t bytes;
215         uint8_t *buf;
216         int err = 0;
217
218         out = fopen(out_path, "w");
219         if (!out) {
220                 fprintf(stderr, "Couldn't open %s\n", out_path);
221                 err = -EACCES;
222                 goto out;
223         }
224
225         buf = malloc(length);
226         if (!buf) {
227                 fprintf(stderr, "Couldn't alloc %zu B buffer\n", length);
228                 err =  -ENOMEM;
229                 goto err_close;
230         }
231
232         fseek(trx, offset, SEEK_SET);
233         bytes = fread(buf, 1, length, trx);
234         if (bytes != length) {
235                 fprintf(stderr, "Couldn't read %zu B of data from %s\n", length, trx_path);
236                 err =  -ENOMEM;
237                 goto err_free_buf;
238         };
239
240         bytes = fwrite(buf, 1, length, out);
241         if (bytes != length) {
242                 fprintf(stderr, "Couldn't write %zu B to %s\n", length, out_path);
243                 err =  -ENOMEM;
244                 goto err_free_buf;
245         }
246
247         printf("Extracted 0x%zx bytes into %s\n", length, out_path);
248
249 err_free_buf:
250         free(buf);
251 err_close:
252         fclose(out);
253 out:
254         return err;
255 }
256
257 static int otrx_extract() {
258         FILE *trx;
259         struct trx_header hdr;
260         size_t bytes;
261         int i;
262         int err = 0;
263
264         trx = fopen(trx_path, "r");
265         if (!trx) {
266                 fprintf(stderr, "Couldn't open %s\n", trx_path);
267                 err = -EACCES;
268                 goto out;
269         }
270
271         fseek(trx, trx_offset, SEEK_SET);
272         bytes = fread(&hdr, 1, sizeof(hdr), trx);
273         if (bytes != sizeof(hdr)) {
274                 fprintf(stderr, "Couldn't read %s header\n", trx_path);
275                 err =  -EIO;
276                 goto err_close;
277         }
278
279         if (le32_to_cpu(hdr.magic) != TRX_MAGIC) {
280                 fprintf(stderr, "Invalid TRX magic: 0x%08x\n", le32_to_cpu(hdr.magic));
281                 err =  -EINVAL;
282                 goto err_close;
283         }
284
285         for (i = 0; i < TRX_MAX_PARTS; i++) {
286                 size_t length;
287
288                 if (!partition[i])
289                         continue;
290                 if (!hdr.offset[i]) {
291                         printf("TRX doesn't contain partition %d, can't extract %s\n", i + 1, partition[i]);
292                         continue;
293                 }
294
295                 if (i + 1 >= TRX_MAX_PARTS || !hdr.offset[i + 1])
296                         length = le32_to_cpu(hdr.length) - le32_to_cpu(hdr.offset[i]);
297                 else
298                         length = le32_to_cpu(hdr.offset[i + 1]) - le32_to_cpu(hdr.offset[i]);
299
300                 otrx_extract_copy(trx, trx_offset + le32_to_cpu(hdr.offset[i]), length, partition[i]);
301         }
302
303 err_close:
304         fclose(trx);
305 out:
306         return err;
307 }
308
309 /**************************************************
310  * Start
311  **************************************************/
312
313 static void parse_options(int argc, char **argv) {
314         int c;
315
316         while ((c = getopt(argc, argv, "c:e:o:1:2:3:")) != -1) {
317                 switch (c) {
318                 case 'c':
319                         mode = MODE_CHECK;
320                         trx_path = optarg;
321                         break;
322                 case 'e':
323                         mode = MODE_EXTRACT;
324                         trx_path = optarg;
325                         break;
326                 case 'o':
327                         trx_offset = atoi(optarg);
328                         break;
329                 case '1':
330                         partition[0] = optarg;
331                         break;
332                 case '2':
333                         partition[1] = optarg;
334                         break;
335                 case '3':
336                         partition[2] = optarg;
337                         break;
338                 }
339         }
340 }
341
342 static void usage() {
343         printf("Usage:\n");
344         printf("\n");
345         printf("Checking TRX file:\n");
346         printf("\t-c file\t\tcheck if file is a valid TRX\n");
347         printf("\t-o offset\toffset of TRX data in file (default: 0)\n");
348         printf("\n");
349         printf("Extracting from TRX file:\n");
350         printf("\t-e file\t\tfile with TRX to extract from\n");
351         printf("\t-o offset\toffset of TRX data in file (default: 0)\n");
352         printf("\t-1 file\t\tfile to extract 1st partition to (optional)\n");
353         printf("\t-2 file\t\tfile to extract 2nd partition to (optional)\n");
354         printf("\t-3 file\t\tfile to extract 3rd partition to (optional)\n");
355 }
356
357 int main(int argc, char **argv) {
358         parse_options(argc, argv);
359
360         switch (mode) {
361         case MODE_CHECK:
362                 return otrx_check();
363         case MODE_EXTRACT:
364                 return otrx_extract();
365         default:
366                 usage();
367         }
368
369         return 0;
370 }