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