firmware-utils: fix coverity zytrx.c resource leak
[openwrt/staging/hauke.git] / tools / firmware-utils / src / asustrx.c
1 /*
2 * asustrx
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 <string.h>
18 #include <unistd.h>
19
20 #if __BYTE_ORDER == __BIG_ENDIAN
21 #define cpu_to_le32(x) bswap_32(x)
22 #define le32_to_cpu(x) bswap_32(x)
23 #elif __BYTE_ORDER == __LITTLE_ENDIAN
24 #define cpu_to_le32(x) (x)
25 #define le32_to_cpu(x) (x)
26 #else
27 #error "Unsupported endianness"
28 #endif
29
30 #define TRX_MAGIC 0x30524448
31 #define TRX_FLAGS_OFFSET 12
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 struct asustrx_tail {
43 uint8_t version[4];
44 char productid[12];
45 uint8_t unused[48];
46 };
47
48 char *in_path = NULL;
49 char *out_path = NULL;
50 char *productid = NULL;
51 uint8_t version[4] = { };
52
53 static const uint32_t crc32_tbl[] = {
54 0x00000000, 0x77073096, 0xee0e612c, 0x990951ba,
55 0x076dc419, 0x706af48f, 0xe963a535, 0x9e6495a3,
56 0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988,
57 0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91,
58 0x1db71064, 0x6ab020f2, 0xf3b97148, 0x84be41de,
59 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7,
60 0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec,
61 0x14015c4f, 0x63066cd9, 0xfa0f3d63, 0x8d080df5,
62 0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172,
63 0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b,
64 0x35b5a8fa, 0x42b2986c, 0xdbbbc9d6, 0xacbcf940,
65 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59,
66 0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116,
67 0x21b4f4b5, 0x56b3c423, 0xcfba9599, 0xb8bda50f,
68 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924,
69 0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d,
70 0x76dc4190, 0x01db7106, 0x98d220bc, 0xefd5102a,
71 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433,
72 0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818,
73 0x7f6a0dbb, 0x086d3d2d, 0x91646c97, 0xe6635c01,
74 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e,
75 0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457,
76 0x65b0d9c6, 0x12b7e950, 0x8bbeb8ea, 0xfcb9887c,
77 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65,
78 0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2,
79 0x4adfa541, 0x3dd895d7, 0xa4d1c46d, 0xd3d6f4fb,
80 0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0,
81 0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9,
82 0x5005713c, 0x270241aa, 0xbe0b1010, 0xc90c2086,
83 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f,
84 0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4,
85 0x59b33d17, 0x2eb40d81, 0xb7bd5c3b, 0xc0ba6cad,
86 0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a,
87 0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683,
88 0xe3630b12, 0x94643b84, 0x0d6d6a3e, 0x7a6a5aa8,
89 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1,
90 0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe,
91 0xf762575d, 0x806567cb, 0x196c3671, 0x6e6b06e7,
92 0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc,
93 0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5,
94 0xd6d6a3e8, 0xa1d1937e, 0x38d8c2c4, 0x4fdff252,
95 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b,
96 0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60,
97 0xdf60efc3, 0xa867df55, 0x316e8eef, 0x4669be79,
98 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236,
99 0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f,
100 0xc5ba3bbe, 0xb2bd0b28, 0x2bb45a92, 0x5cb36a04,
101 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d,
102 0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a,
103 0x9c0906a9, 0xeb0e363f, 0x72076785, 0x05005713,
104 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38,
105 0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21,
106 0x86d3d2d4, 0xf1d4e242, 0x68ddb3f8, 0x1fda836e,
107 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777,
108 0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c,
109 0x8f659eff, 0xf862ae69, 0x616bffd3, 0x166ccf45,
110 0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2,
111 0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db,
112 0xaed16a4a, 0xd9d65adc, 0x40df0b66, 0x37d83bf0,
113 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9,
114 0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6,
115 0xbad03605, 0xcdd70693, 0x54de5729, 0x23d967bf,
116 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94,
117 0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d,
118 };
119
120 static void parse_options(int argc, char **argv) {
121 int c;
122
123 while ((c = getopt(argc, argv, "i:o:p:v:")) != -1) {
124 switch (c) {
125 case 'i':
126 in_path = optarg;
127 break;
128 case 'o':
129 out_path = optarg;
130 break;
131 case 'p':
132 productid = optarg;
133 break;
134 case 'v':
135 if (sscanf(optarg, "%hhu.%hhu.%hhu.%hhu", &version[0], &version[1], &version[2], &version[3]) != 4)
136 fprintf(stderr, "Version %s doesn't match suppored 4-digits format\n", optarg);
137 break;
138 }
139 }
140 }
141
142 static void usage() {
143 printf("Usage:\n");
144 printf("\t-i file\t\t\t\tinput TRX file\n");
145 printf("\t-o file\t\t\t\toutput Asus TRX file\n");
146 printf("\t-p productid\t\t\tproduct (device) ID\n");
147 printf("\t-v version\t\t\tfirmware version formatted with 4 digits like: 1.2.3.4\n");
148 }
149
150 int main(int argc, char **argv) {
151 struct trx_header hdr;
152 struct asustrx_tail tail = { };
153 FILE *out = NULL;
154 FILE *in = NULL;
155 uint8_t buf[1024];
156 size_t bytes;
157 size_t length = 0;
158 uint32_t crc32 = 0xffffffff;
159 int i;
160 int err = 0;
161
162 /* Parse & validate arguments */
163 parse_options(argc, argv);
164 if (!in_path || !out_path || !productid) {
165 usage();
166 err = -EINVAL;
167 goto err;
168 }
169
170 /* Fill Asus tail */
171 tail.version[0] = version[0];
172 tail.version[1] = version[1];
173 tail.version[2] = version[2];
174 tail.version[3] = version[3];
175 strncpy(tail.productid, productid, sizeof(tail.productid));
176
177 /* Open files */
178 in = fopen(in_path, "r");
179 if (!in) {
180 fprintf(stderr, "Couldn't open %s\n", in_path);
181 err = -EIO;
182 goto err;
183 }
184 out = fopen(out_path, "w+");
185 if (!out) {
186 fprintf(stderr, "Couldn't open %s\n", out_path);
187 err = -EIO;
188 goto err;
189 }
190
191 /* Check is there is empty place for Asus tail */
192 bytes = sizeof(struct asustrx_tail);
193 fseek(in, -bytes, SEEK_END);
194 if (fread(buf, 1, bytes, in) != bytes) {
195 fprintf(stderr, "Couldn't read %zu B from %s\n", bytes, in_path);
196 err = -EIO;
197 goto err;
198 }
199 for (i = 0; i < bytes; i++) {
200 if (buf[i]) {
201 fprintf(stderr, "Input TRX doesn't have last 64 B empty %s\n", out_path);
202 err = -ENOSPC;
203 goto err;
204 }
205 }
206
207 /* Copy whole TRX */
208 rewind(in);
209 while ((bytes = fread(buf, 1, sizeof(buf), in)) > 0) {
210 if (fwrite(buf, 1, bytes, out) != bytes) {
211 fprintf(stderr, "Couldn't write %zu B to %s\n", bytes, out_path);
212 err = -EIO;
213 goto err;
214 }
215 }
216
217 /* Overwrite last 64 B with Asus tail */
218 bytes = sizeof(tail);
219 fseek(out, -bytes, SEEK_CUR);
220 if (fwrite(&tail, 1, bytes, out) != bytes) {
221 fprintf(stderr, "Couldn't write %zu B to %s\n", bytes, out_path);
222 err = -EIO;
223 goto err;
224 }
225
226 /* Calculate crc32 */
227 fseek(out, TRX_FLAGS_OFFSET, SEEK_SET);
228 length = TRX_FLAGS_OFFSET;
229 while ((bytes = fread(buf, 1, sizeof(buf), out )) > 0) {
230 length += bytes;
231 for (i = 0; i < bytes; i++)
232 crc32 = crc32_tbl[(crc32 ^ buf[i]) & 0xff] ^ (crc32 >> 8);
233 }
234
235 /* Update header */
236 bytes = sizeof(hdr);
237 rewind(out);
238 if (fread(&hdr, 1, sizeof(hdr), out) != bytes) {
239 fprintf(stderr, "Couldn't read %zu B from %s\n", bytes, in_path);
240 err = -EIO;
241 goto err;
242 }
243 hdr.crc32 = cpu_to_le32(crc32);
244 rewind(out);
245 if (fwrite(&hdr, 1, bytes, out) != bytes) {
246 fprintf(stderr, "Couldn't write %zu B to %s\n", bytes, out_path);
247 err = -EIO;
248 goto err;
249 }
250
251 err:
252 if (out)
253 fclose(out);
254 if (in)
255 fclose(in);
256 return err;
257 }