wrt350nv2-builder: Fix memory leak
[openwrt/openwrt.git] / tools / wrt350nv2-builder / src / wrt350nv2-builder.c
1 /*
2
3 WRT350Nv2-Builder 2.4 (previously called buildimg)
4 Copyright (C) 2008-2009 Dirk Teurlings <info@upexia.nl>
5 Copyright (C) 2009-2011 Matthias Buecher (http://www.maddes.net/)
6
7 This program is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 2 of the License, or
10 (at your option) any later version.
11
12 This program is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
16
17 You should have received a copy of the GNU General Public License
18 along with this program; if not, write to the Free Software
19 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20
21 A lot of thanks to Kaloz and juhosg from OpenWRT and Lennert Buytenhek from
22 marvell for helping me figure this one out. This code is based on bash
23 scripts wrote by Peter van Valderen so the real credit should go to him.
24
25 This program reads the provided parameter file and creates an image which can
26 be used to flash a Linksys WRT350N v2 from stock firmware.
27 The trick is to fill unused space in the bin file with random, so that the
28 resulting zip file passes the size check of the stock firmware.
29
30 The parameter file layout for an original Linksys firmware:
31 :kernel 0x001A0000 /path/to/uImage
32 :rootfs 0 /path/to/root.squashfs
33 :u-boot 0 /path/to/u-boot.bin
34 #version 0x2020
35
36 Additionally since v2.4 an already complete image can be used:
37 :image 0 /path/to/openwrt-wrt350nv2-[squashfs|jffs2-64k].img
38
39 args:
40 1 wrt350nv2.par parameter file describing the image layout
41 2 wrt350nv2.img output file for linksys style image
42
43 A u-boot image inside the bin file is not necessary.
44 The version is not important.
45 The name of the bin file is not important, but still "wrt350n.bin" is used to
46 keep as close as possible to the stock firmware.
47
48 Linksys assumes that no mtd will be used to its maximum, so the last 16 bytes
49 of the mtd are abused to define the length of the next mtd content (4 bytes for
50 size + 12 pad bytes).
51
52 At the end of "rootfs" additional 16 bytes are abused for some data and a
53 highly important eRcOmM identifier, so the last 32 bytes of "rootfs" are abused.
54
55 At the end of "u-boot" 128 bytes are abused for some data, a checksum and a
56 highly important sErCoMm identifier.
57
58
59 This program uses a special GNU scanf modifier to allocate
60 sufficient memory for a strings with unknown length.
61 See http://www.kernel.org/doc/man-pages/online/pages/man3/scanf.3.html#NOTES
62
63
64 To extract everything from a Linksys style firmware image see
65 https://forum.openwrt.org/viewtopic.php?pid=92928#p92928
66
67 Changelog:
68 v2.4 - added ":image" definition for parameter file, this allows
69 to use a complete sysupgrade image without any kernel size check
70 v2.3 - allow jffs by adding its magic number (0x8519)
71 added parameter option -i to ignore unknown magic numbers
72 v2.2 - fixed checksum byte calculation for other versions than 0x2019
73 fixed rare problem with padsize
74 updated info to stock firmware 2.00.20
75 fixed typos
76 v2.1 - used "wrt350n.bin" for the created image (closer to stock)
77 added option to create the image in two separate steps (-b / -z)
78 v2.0 - complete re-write
79
80 */
81
82 // includes
83 #define _GNU_SOURCE // for GNU's basename()
84 #include <assert.h>
85 #include <errno.h> // errno
86 #include <stdarg.h>
87 #include <stdio.h> // fopen(), fread(), fclose(), etc.
88 #include <stdlib.h> // system(), etc.
89 #include <string.h> // basename(), strerror(), strdup(), etc.
90 #include <unistd.h> // optopt(), access(), etc.
91 #include <libgen.h>
92 #include <sys/wait.h> // WEXITSTATUS, etc.
93
94 // custom includes
95 #include "md5.h" // MD5 routines
96 #include "upgrade.h" // Linksys definitions from firmware 2.0.19 (unchanged up to 2.0.20)
97
98
99 // version info
100 #define VERSION "2.4"
101 char program_info[] = "WRT350Nv2-Builder v%s by Dirk Teurlings <info@upexia.nl> and Matthias Buecher (http://www.maddes.net/)\n";
102
103 // verbosity
104 #define DEBUG 1
105 #define DEBUG_LVL2 2
106 int verbosity = 0;
107
108 // mtd info
109 typedef struct {
110 char *name;
111 int offset;
112 int size;
113 char *filename;
114 long int filesize;
115 unsigned char magic[2];
116 } mtd_info;
117
118 mtd_info mtd_kernel = { "kernel", 0, 0, NULL, 0L, { 0, 0 } };
119 mtd_info mtd_rootfs = { "rootfs", 0, 0, NULL, 0L, { 0, 0 } };
120 mtd_info mtd_image = { "image", 0, 0, NULL, 0L, { 0, 0 } };
121 mtd_info mtd_uboot = { "u-boot", 0, 0, NULL, 0L, { 0, 0 } };
122
123 #define ROOTFS_END_OFFSET 0x00760000
124 #define ROOTFS_MIN_OFFSET 0x00640000 // should be filled up to here, to make sure that the zip file is big enough to pass the size check of the stock firmware
125 // 2.0.17: filled up to 0x00640000
126 // 2.0.19: filled up to 0x00670000
127 // 2.0.20: filled up to 0x00670000
128
129 // rootfs statics via: hexdump -v -e '1/1 "0x%02X, "' -s 0x0075FFE0 -n 16 "wrt350n.bin" ; echo -en "\n"
130 unsigned char product_id[] = { 0x00, 0x03 }; // seems to be a fixed value
131 unsigned char protocol_id[] = { 0x00, 0x00 }; // seems to be a fixed value
132 unsigned char fw_version[] = { 0x20, 0x20 };
133 unsigned char rootfs_unknown[] = { 0x90, 0xF7 }; // seems to be a fixed value
134 unsigned char sign[] = { 0x65, 0x52, 0x63, 0x4F, 0x6D, 0x4D, 0x00, 0x00 }; // eRcOmM
135
136 // u-boot statics via: hexdump -v -e '1/1 "0x%02X, "' -s 0x007FFF80 -n 128 "wrt350n.bin" ; echo -en "\n"
137 //unsigned char sn[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; // (12) seems to be an unused value
138 //unsigned char pin[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; // (8) seems to be an unused value
139 //unsigned char node[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // (25) seems to be an unused value
140 // 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
141 //unsigned char checksum[] = { 0xE9 }; // (1) is calculated, does it belong to node?
142 unsigned char pid[] = { 0x73, 0x45, 0x72, 0x43, 0x6F, 0x4D, 0x6D, 0x00, 0x01, 0x00, 0x00, 0x59, 0x42, 0x50, 0x00, 0x01, // (70) seems to be a fixed value, except for fw version
143 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
144 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, // protocol id?
145 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, // protocol id?
146 0x12, 0x34, // firmware version, same as in rootfs
147 0x00, 0x00, 0x00, 0x04,
148 0x73, 0x45, 0x72, 0x43, 0x6F, 0x4D, 0x6D }; // sErCoMm
149
150 // img statics via: hexdump -v -e '1/1 "0x%02X, "' -s 0 -n 512 "WRT350N-EU-ETSI-2.00.19.img" ; echo -en "\n" (unchanged up to 2.0.20)
151 unsigned char img_hdr[] = { 0x00, 0x01, 0x00, 0x00, 0x59, 0x42, 0x50, 0x00, 0x01, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
152 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
153 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03,
154 0x00, 0x00,
155 0x12, 0x34, // firmware version, same as in rootfs
156 0x00, 0x00, 0x00, 0x04, 0x61, 0x44, 0x6D, 0x42, 0x6C, 0x4B, 0x3D, 0x00,
157 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
158 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
159 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
160 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
161 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
162 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
163 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
164 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
165 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
166 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
167 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
168 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
169 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
170 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
171 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
172 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
173 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
174 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
175 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
176 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
177 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
178 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
179 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
180 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
181 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
182 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
183 0x12, 0x34, 0x56, 0x78, 0x9A, 0xBC, 0xDE, 0xF0, 0x12, 0x34, 0x56, 0x78, 0x9A, 0xBC, 0xDE, 0xF0, // md5 checksum
184 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
185
186 unsigned char img_eof[] = { 0xFF };
187
188
189 void lprintf(int outputlevel, char *fmt, ...) {
190 va_list argp;
191 if (outputlevel <= verbosity) {
192 va_start(argp, fmt);
193 vprintf(fmt, argp);
194 va_end(argp);
195 }
196 }
197
198
199 int parse_par_file(FILE *f_par) {
200 int exitcode = 0;
201
202 char *buffer;
203 size_t buffer_size;
204 char *line;
205
206 int lineno;
207 int count;
208
209 char string1[256];
210 char string2[256];
211 int value;
212
213 mtd_info *mtd;
214 FILE *f_in;
215 int f_exitcode = 0;
216
217 // read all lines
218 buffer_size = 1000;
219 buffer = NULL;
220 lineno = 0;
221 while (!feof(f_par)) {
222 // read next line into memory
223 do {
224 // allocate memory for input line
225 if (!buffer) {
226 buffer = malloc(buffer_size);
227 }
228 if (!buffer) {
229 exitcode = 1;
230 printf("parse_par_file: can not allocate %i bytes\n", (int) buffer_size);
231 break;
232 }
233
234 line = fgets(buffer, buffer_size, f_par);
235 if (!line) {
236 exitcode = ferror(f_par);
237 if (exitcode) {
238 printf("parse_par_file: %s\n", strerror(exitcode));
239 }
240 break;
241 }
242
243 // if buffer was not completely filled, then assume that line is complete
244 count = strlen(buffer) + 1;
245 if (count-- < buffer_size) {
246 break;
247 }
248
249 // otherwise....
250
251 // reset file position to line start
252 value = fseek(f_par, -count, SEEK_CUR);
253 if (value == -1) {
254 exitcode = errno;
255 printf("parse_par_file: %s\n", strerror(exitcode));
256 break;
257 }
258
259 // double buffer size
260 free(buffer);
261 buffer = NULL;
262 buffer_size *= 2;
263 lprintf(DEBUG_LVL2, " extending buffer to %i bytes\n", buffer_size);
264 } while (1);
265 if ((!line) || (exitcode)) {
266 break;
267 }
268
269 lineno++; // increase line number
270
271 lprintf(DEBUG_LVL2, " line %i (%i) %s", lineno, count, line);
272
273 value = 0;
274 mtd = NULL;
275
276 // split line if starting with a colon
277 switch (line[0]) {
278 case ':':
279 count = sscanf(line, ":%255s %i %255s", string1, &value, string2);
280 if (count != 3) {
281 printf("line %i does not meet defined format (:<mtdname> <mtdsize> <file>)\n", lineno);
282 } else {
283 // populate mtd_info if supported mtd names
284 if (!strcmp(string1, mtd_kernel.name)) {
285 mtd = &mtd_kernel;
286 } else if (!strcmp(string1, mtd_rootfs.name)) {
287 mtd = &mtd_rootfs;
288 } else if (!strcmp(string1, mtd_uboot.name)) {
289 mtd = &mtd_uboot;
290 } else if (!strcmp(string1, mtd_image.name)) {
291 mtd = &mtd_image;
292 }
293
294 if (!mtd) {
295 printf("unknown mtd %s in line %i\n", string1, lineno);
296 } else if (mtd->filename) {
297 f_exitcode = 1;
298 printf("mtd %s in line %i multiple definitions\n", string1, lineno);
299 } else {
300 mtd->size = value;
301 mtd->filename = strdup(string2);
302
303 // Get file size
304 f_in = fopen(mtd->filename, "rb");
305 if (!f_in) {
306 f_exitcode = errno;
307 printf("input file %s: %s\n", mtd->filename, strerror(f_exitcode));
308 } else {
309 value = fread(&mtd->magic, 1, 2, f_in);
310 if (value < 2) {
311 if (ferror(f_in)) {
312 f_exitcode = ferror(f_in);
313 printf("input file %s: %s\n", mtd->filename, strerror(f_exitcode));
314 } else {
315 f_exitcode = 1;
316 printf("input file %s: smaller than two bytes, no magic code\n", mtd->filename);
317 }
318 }
319
320 value = fseek(f_in, 0, SEEK_END);
321 if (value == -1) {
322 f_exitcode = errno;
323 printf("input file %s: %s\n", mtd->filename, strerror(f_exitcode));
324 } else {
325 mtd->filesize = ftell(f_in);
326 if (mtd->filesize == -1) {
327 f_exitcode = errno;
328 printf("input file %s: %s\n", mtd->filename, strerror(f_exitcode));
329 }
330 }
331
332 fclose(f_in);
333 }
334
335 lprintf(DEBUG, "mtd %s in line %i: size=0x%08X, filesize=0x%08lX, magic=0x%02X%02X, file=%s\n", mtd->name, lineno, mtd->size, mtd->filesize, mtd->magic[0], mtd->magic[1], mtd->filename);
336 }
337 }
338 break;
339 case '#': // integer values
340 count = sscanf(line, "#%255s %i", string1, &value);
341 if (count != 2) {
342 printf("line %i does not meet defined format (#<variable name> <integer>\n", lineno);
343 } else {
344 if (!strcmp(string1, "version")) {
345 // changing version
346 fw_version[0] = 0x000000FF & ( value >> 8 );
347 fw_version[1] = 0x000000FF & value;
348 } else {
349 printf("unknown integer variable %s in line %i\n", string1, lineno);
350 }
351
352 lprintf(DEBUG, "integer variable %s in line %i: 0x%08X\n", string1, lineno, value);
353 }
354 break;
355 case '$': // strings
356 count = sscanf(line, "$%255s %255s", string1, string2);
357 if (count != 2) {
358 printf("line %i does not meet defined format (:<mtdname> <mtdsize> <file>)\n", lineno);
359 } else {
360 /*
361 if (!strcmp(string1, "something")) {
362 something = strdup(string2);
363 } else {
364 */
365 printf("unknown string variable %s in line %i\n", string1, lineno);
366 // }
367 lprintf(DEBUG, "string variable %s in line %i: %s\n", string1, lineno, string2);
368 }
369 break;
370 default:
371 break;
372 }
373 }
374 free(buffer);
375
376 if (!exitcode) {
377 exitcode = f_exitcode;
378 }
379
380 return exitcode;
381 }
382
383
384 int create_bin_file(char *bin_filename) {
385 int exitcode = 0;
386
387 unsigned char *buffer;
388
389 int i;
390 mtd_info *mtd;
391 int addsize;
392 int padsize;
393
394 char *rand_filename = "/dev/urandom";
395 FILE *f_in;
396 int size;
397
398 unsigned long int csum;
399 unsigned char checksum;
400
401 FILE *f_out;
402
403 // allocate memory for bin file
404 buffer = malloc(KERNEL_CODE_OFFSET + FLASH_SIZE);
405 if (!buffer) {
406 exitcode = 1;
407 printf("create_bin_file: can not allocate %i bytes\n", FLASH_SIZE);
408 } else {
409 // initialize with zero
410 memset(buffer, 0, KERNEL_CODE_OFFSET + FLASH_SIZE);
411 }
412
413 // add files
414 if (!exitcode) {
415 for (i = 1; i <= 4; i++) {
416 addsize = 0;
417 padsize = 0;
418
419 switch (i) {
420 case 1:
421 mtd = &mtd_image;
422 padsize = ROOTFS_MIN_OFFSET - mtd->filesize;
423 break;
424 case 2:
425 mtd = &mtd_kernel;
426 break;
427 case 3:
428 mtd = &mtd_rootfs;
429 addsize = mtd->filesize;
430 padsize = ROOTFS_MIN_OFFSET - mtd_kernel.size - mtd->filesize;
431 break;
432 case 4:
433 mtd = &mtd_uboot;
434 addsize = mtd->filesize;
435 break;
436 default:
437 mtd = NULL;
438 exitcode = 1;
439 printf("create_bin_file: unknown mtd %i\n", i);
440 break;
441 }
442 if (!mtd) {
443 break;
444 }
445 if (!mtd->filename) {
446 continue;
447 }
448
449 lprintf(DEBUG, "adding mtd %s file %s\n", mtd->name, mtd->filename);
450
451 // adding file size
452 if (addsize) {
453 buffer[KERNEL_CODE_OFFSET + mtd->offset - 16] = 0x000000FFL & ( addsize >> 24 );
454 buffer[KERNEL_CODE_OFFSET + mtd->offset - 15] = 0x000000FFL & ( addsize >> 16 );
455 buffer[KERNEL_CODE_OFFSET + mtd->offset - 14] = 0x000000FFL & ( addsize >> 8 );
456 buffer[KERNEL_CODE_OFFSET + mtd->offset - 13] = 0x000000FFL & addsize;
457 }
458
459 // adding file content
460 f_in = fopen(mtd->filename, "rb");
461 if (!f_in) {
462 exitcode = errno;
463 printf("input file %s: %s\n", mtd->filename, strerror(exitcode));
464 } else {
465 size = fread(&buffer[KERNEL_CODE_OFFSET + mtd->offset], mtd->filesize, 1, f_in);
466 if (size < 1) {
467 if (ferror(f_in)) {
468 exitcode = ferror(f_in);
469 printf("input file %s: %s\n", mtd->filename, strerror(exitcode));
470 } else {
471 exitcode = 1;
472 printf("input file %s: smaller than before *doh*\n", mtd->filename);
473 }
474 }
475 fclose(f_in);
476 }
477
478 // padding
479 if (padsize > 0) {
480 addsize = padsize & 0x0000FFFF; // start on next 64KB border
481 padsize -= addsize;
482 }
483 if (padsize > 0) {
484 printf("mtd %s input file %s is too small (0x%08lX), adding 0x%08X random bytes\n", mtd->name, mtd->filename, mtd->filesize, padsize);
485
486 addsize += KERNEL_CODE_OFFSET + mtd->offset + mtd->filesize; // get offset
487 lprintf(DEBUG, " padding offset 0x%08X length 0x%08X\n", addsize, padsize);
488
489 f_in = fopen(rand_filename, "rb");
490 if (!f_in) {
491 exitcode = errno;
492 printf("input file %s: %s\n", rand_filename, strerror(exitcode));
493 } else {
494 size = fread(&buffer[addsize], padsize, 1, f_in);
495 if (size < 1) {
496 if (ferror(f_in)) {
497 exitcode = ferror(f_in);
498 printf("input file %s: %s\n", rand_filename, strerror(exitcode));
499 } else {
500 exitcode = 1;
501 printf("input file %s: smaller than before *doh*\n", rand_filename);
502 }
503 }
504 }
505 fclose(f_in);
506 }
507 }
508 }
509
510 // add special contents
511 if (!exitcode) {
512 lprintf(DEBUG, "adding rootfs special data\n");
513 memcpy(&buffer[KERNEL_CODE_OFFSET + PRODUCT_ID_OFFSET], product_id, 2);
514 memcpy(&buffer[KERNEL_CODE_OFFSET + PROTOCOL_ID_OFFSET], protocol_id, 2);
515 memcpy(&buffer[KERNEL_CODE_OFFSET + FW_VERSION_OFFSET], fw_version, 2);
516 memcpy(&buffer[KERNEL_CODE_OFFSET + FW_VERSION_OFFSET + 2], rootfs_unknown, 2);
517 memcpy(&buffer[KERNEL_CODE_OFFSET + SIGN_OFFSET], sign, 8); // eRcOmM
518
519 lprintf(DEBUG, "adding u-boot special data\n");
520 // memcpy(&buffer[KERNEL_CODE_OFFSET + SN_OFF], sn, 12); // ToDo: currently zero, find out what's this for?
521 // memcpy(&buffer[KERNEL_CODE_OFFSET + PIN_OFF], pin, 8); // ToDo: currently zero, find out what's this for?
522 // memcpy(&buffer[KERNEL_CODE_OFFSET + NODE_BASE_OFF], node, 25); // ToDo: currently zero, find out what's this for?
523 memcpy(&buffer[KERNEL_CODE_OFFSET + BOOT_ADDR_BASE_OFF + PID_OFFSET], pid, 70); // sErCoMm
524 memcpy(&buffer[KERNEL_CODE_OFFSET + BOOT_ADDR_BASE_OFF + PID_OFFSET + 57], fw_version, 2);
525
526 lprintf(DEBUG, "adding checksum byte\n");
527 csum = 0;
528 for (i = 0; i < KERNEL_CODE_OFFSET + FLASH_SIZE; i++) {
529 csum += buffer[i];
530 }
531 lprintf(DEBUG_LVL2, " checksum 0x%016lX (%li)\n", csum, csum);
532
533 buffer[KERNEL_CODE_OFFSET + NODE_BASE_OFF + 25] = ~csum + 1;
534 lprintf(DEBUG, " byte 0x%02X\n", buffer[KERNEL_CODE_OFFSET + NODE_BASE_OFF + 25]);
535 }
536
537 // write bin file
538 if (!exitcode) {
539 lprintf(DEBUG, "writing file %s\n", bin_filename);
540 f_out = fopen(bin_filename, "wb");
541 if (!f_out) {
542 exitcode = errno;
543 printf("output file %s: %s\n", bin_filename, strerror(exitcode));
544 } else {
545 size = fwrite(buffer, KERNEL_CODE_OFFSET + FLASH_SIZE, 1, f_out);
546 if (size < 1) {
547 if (ferror(f_out)) {
548 exitcode = ferror(f_out);
549 printf("output file %s: %s\n", bin_filename, strerror(exitcode));
550 } else {
551 exitcode = 1;
552 printf("output file %s: unspecified write error\n", bin_filename);
553 }
554 }
555 fclose(f_out);
556 }
557 }
558
559 free(buffer);
560 return exitcode;
561 }
562
563
564 int create_zip_file(char *zip_filename, char *bin_filename) {
565 int exitcode = 0;
566
567 char *buffer;
568 size_t buffer_size;
569 int count;
570
571 buffer_size = 1000;
572 buffer = NULL;
573 do {
574 // allocate memory for command line
575 if (!buffer) {
576 buffer = malloc(buffer_size);
577 }
578 if (!buffer) {
579 exitcode = 1;
580 printf("create_zip_file: can not allocate %i bytes\n", (int) buffer_size);
581 break;
582 }
583
584 // if buffer was not completely filled, then line fit in completely
585 count = snprintf(buffer, buffer_size, "zip \"%s\" \"%s\"", zip_filename, bin_filename);
586 if ((count > -1) && (count < buffer_size)) {
587 break;
588 }
589
590 // otherwise try again with more space
591 if (count > -1) { // glibc 2.1
592 buffer_size = count + 1; // precisely what is needed
593 } else { // glibc 2.0
594 buffer_size *= 2; // twice the old size
595 }
596 free(buffer);
597 buffer = NULL;
598 lprintf(DEBUG_LVL2, " extending buffer to %i bytes\n", buffer_size);
599 } while (1);
600
601 if (!exitcode) {
602 // zipping binfile
603 lprintf(DEBUG, "%s\n", buffer);
604 count = system(buffer);
605 if ((count < 0) || (WEXITSTATUS(count))) {
606 exitcode = 1;
607 printf("create_zip_file: can not execute %s bytes\n", buffer);
608 }
609 }
610
611 return exitcode;
612 }
613
614
615 int create_img_file(FILE *f_out, char *out_filename, char *zip_filename) {
616 int exitcode = 0;
617
618 md5_state_t state;
619 md5_byte_t digest[16];
620
621 int i;
622 int size;
623
624 FILE *f_in;
625 unsigned char buffer[1];
626
627 // copy firmware version
628 memcpy(&img_hdr[50], fw_version, 2);
629
630 // clear md5 checksum
631 memset(&img_hdr[480], 0, 16);
632
633 // prepare md5 checksum calculation
634 md5_init(&state);
635
636 // add img header
637 lprintf(DEBUG_LVL2, " adding img header\n");
638 for (i = 0; i < 512; i++) {
639 size = fputc(img_hdr[i], f_out);
640 if (size == EOF) {
641 exitcode = ferror(f_out);
642 printf("output file %s: %s\n", out_filename, strerror(exitcode));
643 break;
644 }
645 md5_append(&state, (const md5_byte_t *)&img_hdr[i], 1);
646 }
647
648 // adding zip file
649 if (!exitcode) {
650 lprintf(DEBUG_LVL2, " adding zip file\n");
651 f_in = fopen(zip_filename, "rb");
652 if (!f_in) {
653 exitcode = errno;
654 printf("input file %s: %s\n", zip_filename, strerror(exitcode));
655 } else {
656 while ((size = fgetc(f_in)) != EOF) {
657 buffer[0] = size;
658
659 size = fputc(buffer[0], f_out);
660 if (size == EOF) {
661 exitcode = ferror(f_out);
662 printf("output file %s: %s\n", out_filename, strerror(exitcode));
663 break;
664 }
665 md5_append(&state, (const md5_byte_t *)buffer, 1);
666 }
667 if (ferror(f_in)) {
668 exitcode = ferror(f_in);
669 printf("input file %s: %s\n", zip_filename, strerror(exitcode));
670 }
671 }
672
673 }
674
675 // add end byte
676 if (!exitcode) {
677 lprintf(DEBUG_LVL2, " adding img eof byte\n");
678 size = fputc(img_eof[0], f_out);
679 if (size == EOF) {
680 exitcode = ferror(f_out);
681 printf("output file %s: %s\n", out_filename, strerror(exitcode));
682 }
683 md5_append(&state, (const md5_byte_t *)img_eof, 1);
684 }
685
686 // append salt to md5 checksum
687 md5_append(&state, (const md5_byte_t *)"A^gU*<>?RFY@#DR&Z", 17);
688
689 // finish md5 checksum calculation
690 md5_finish(&state, digest);
691
692 // write md5 checksum into img header
693 if (!exitcode) {
694 lprintf(DEBUG_LVL2, " writing md5 checksum into img header of file\n");
695
696 size = fseek(f_out, 480, SEEK_SET);
697 if (size == -1) {
698 exitcode = errno;
699 printf("output file %s: %s\n", out_filename, strerror(exitcode));
700 } else {
701 size = fwrite(digest, 16, 1, f_out);
702 if (size < 1) {
703 if (ferror(f_out)) {
704 exitcode = ferror(f_out);
705 printf("output file %s: %s\n", out_filename, strerror(exitcode));
706 } else {
707 exitcode = 1;
708 printf("output file %s: unspecified write error\n", out_filename);
709 }
710 }
711 }
712
713 fclose(f_in);
714 }
715
716 return exitcode;
717 }
718
719
720 int main(int argc, char *argv[]) {
721 int exitcode = 0;
722
723 int help;
724 int onlybin;
725 int havezip;
726 int ignoremagic;
727 char option;
728 char *par_filename = NULL;
729 char *img_filename = NULL;
730 char *base_filename = NULL;
731 char *bin_filename = NULL;
732 char *zip_filename = NULL;
733
734 FILE *f_par = NULL;
735 FILE *f_img = NULL;
736
737 int i;
738 mtd_info *mtd;
739 int noupdate;
740 int sizecheck;
741 int magiccheck;
742 int magicerror;
743
744
745 // display program header
746 printf(program_info, VERSION);
747
748
749 // command line processing
750 // options
751 help = 0;
752 onlybin = 0;
753 havezip = 0;
754 ignoremagic = 0;
755 while ((option = getopt(argc, argv, "hbzif:v")) != -1) {
756 switch(option) {
757 case 'h':
758 help = 1;
759 break;
760 case 'b':
761 onlybin = 1;
762 break;
763 case 'z':
764 havezip = 1;
765 break;
766 case 'i':
767 ignoremagic = 1;
768 break;
769 case 'f':
770 sizecheck = sscanf(optarg, "%i", &i);
771 if (sizecheck != 1) {
772 printf("Firmware version of -f option not a valid integer\n");
773 exitcode = 1;
774 } else {
775 fw_version[0] = 0x000000FF & ( i >> 8 );
776 fw_version[1] = 0x000000FF & i;
777 }
778 break;
779 case 'v':
780 verbosity++;
781 break;
782 case ':': // option with missing operand
783 printf("Option -%c requires an operand\n", optopt);
784 exitcode = 1;
785 break;
786 case '?':
787 printf("Unrecognized option: -%c\n", optopt);
788 exitcode = 1;
789 break;
790 }
791 }
792
793 // files
794 for ( ; optind < argc; optind++) {
795 if (!par_filename) {
796 par_filename = argv[optind];
797
798 if (access(par_filename, R_OK)) {
799 if (havezip) {
800 printf("No read access to zip file %s\n", par_filename);
801 } else {
802 printf("No read access to parameter or zip file %s\n", par_filename);
803 }
804 exitcode = 1;
805 }
806
807 continue;
808 }
809
810 if ((!onlybin) && (!img_filename)) {
811 img_filename = argv[optind];
812
813 if (!access(img_filename, F_OK)) { // if file already exists then check write access
814 if (access(img_filename, W_OK)) {
815 printf("No write access to image file %s\n", img_filename);
816 exitcode = 1;
817 }
818 }
819
820 continue;
821 }
822
823 printf("Too many files stated\n");
824 exitcode = 1;
825 break;
826 }
827
828 // file name checks
829 if (!par_filename) {
830 if (havezip) {
831 printf("Zip file not stated\n");
832 } else {
833 printf("Parameter file not stated\n");
834 }
835 exitcode = 1;
836 } else {
837 base_filename = basename(par_filename);
838 if (!base_filename) {
839 if (havezip) {
840 printf("Zip file is a directory\n");
841 } else {
842 printf("Parameter file is a directory\n");
843 }
844 exitcode = 1;
845 }
846 }
847
848 if (!onlybin) {
849 if (!img_filename) {
850 printf("Image file not stated\n");
851 exitcode = 1;
852 } else {
853 base_filename = basename(img_filename);
854 if (!base_filename) {
855 printf("Image file is a directory\n");
856 exitcode = 1;
857 }
858 }
859 }
860
861 // check for mutually exclusive options
862 if ((onlybin) && (havezip)) {
863 printf("Option -b and -z are mutually exclusive\n");
864 exitcode = 1;
865 }
866
867 // react on option problems or help request, then exit
868 if ((exitcode) || (help)) {
869 if (help) {
870 printf("This program creates Linksys style images for the WRT350Nv2 router.\n");
871 }
872 printf(" Usage:\n\
873 %s [-h] [-b] [-z] [-i] [-f <version>] [-v] <parameter or zip file> [<image file>]\n\n\
874 Options:\n\
875 -h - Show this help\n\
876 -b - Create only bin file, no img or zip file is created\n\
877 -z - Have zip file, the img file will be directly created from it\n\
878 -i - Ignore unknown magic numbers\n\
879 -f <version> - Wanted firmware version to use with -z\n\
880 Default firmware version is 0x2020 = 2.00.20.\n\
881 Note: version from parameter file will supersede this\n\
882 -v - Increase debug verbosity level\n\n\
883 Example:\n\
884 %s wrt350nv2.par wrt350nv2.img\n\n", argv[0], argv[0]);
885 return exitcode;
886 }
887
888 // handle special case when zipfile is stated
889 if (havezip) {
890 zip_filename = par_filename;
891 par_filename = NULL;
892 }
893
894 lprintf(DEBUG_LVL2, " Verbosity: %i\n", verbosity);
895 lprintf(DEBUG_LVL2, " Program: %s\n", argv[0]);
896
897 if (par_filename) {
898 lprintf(DEBUG, "Parameter file: %s\n", par_filename);
899 }
900 if (zip_filename) {
901 lprintf(DEBUG, "Zip file: %s\n", zip_filename);
902 }
903 if (img_filename) {
904 lprintf(DEBUG, "Image file: %s\n", img_filename);
905 }
906
907
908 // open files from command line
909 // parameter/zip file
910 if (par_filename) {
911 f_par = fopen(par_filename, "rt");
912 if (!f_par) {
913 exitcode = errno;
914 printf("Input file %s: %s\n", par_filename, strerror(exitcode));
915 }
916 }
917
918 // image file
919 if (img_filename) {
920 f_img = fopen(img_filename, "wb");
921 if (!f_img) {
922 exitcode = errno;
923 printf("Output file %s: %s\n", img_filename, strerror(exitcode));
924 }
925 }
926
927 if (exitcode) {
928 return exitcode;
929 }
930
931
932 // parameter file processing
933 if ((!exitcode) && (f_par)) {
934 lprintf(DEBUG, "parsing parameter file...\n");
935
936 exitcode = parse_par_file(f_par);
937
938 lprintf(DEBUG, "...done parsing file\n");
939 }
940 if (f_par) {
941 fclose(f_par);
942 }
943
944
945 // check all input data
946 if ((!exitcode) && (par_filename)) {
947 lprintf(DEBUG, "checking mtd data...\n");
948
949 for (i = 1; i <= 4; i++) {
950 noupdate = 0;
951 sizecheck = 0;
952 magiccheck = 0;
953
954 switch (i) {
955 case 1:
956 mtd = &mtd_image;
957 sizecheck = ROOTFS_END_OFFSET;
958 magiccheck = 1;
959 break;
960 case 2:
961 mtd = &mtd_kernel;
962 sizecheck = mtd_kernel.size - 16;
963 magiccheck = 1;
964 break;
965 case 3:
966 mtd = &mtd_rootfs;
967 mtd->offset = mtd_kernel.size;
968 mtd->size = ROOTFS_END_OFFSET - mtd_kernel.size;
969 sizecheck = PRODUCT_ID_OFFSET - mtd_kernel.size;
970 magiccheck = 1;
971 break;
972 case 4:
973 mtd = &mtd_uboot;
974 mtd->offset = BOOT_ADDR_BASE_OFF;
975 noupdate = 1;
976 sizecheck = SN_OFF - BOOT_ADDR_BASE_OFF;
977 break;
978 default:
979 mtd = NULL;
980 exitcode = 1;
981 printf("unknown mtd check %i\n", i);
982 break;
983 }
984 if (!mtd) {
985 break;
986 }
987
988 lprintf(DEBUG_LVL2, " checking mtd %s\n", mtd->name);
989
990 // general checks
991
992 // no further checks if no file data present
993 if (!mtd->filename) {
994 continue;
995 }
996
997 // not updated by stock firmware
998 if (noupdate) {
999 printf("mtd %s is specified, but will not be updated as of Linksys firmware 2.0.19\n", mtd->name);
1000 }
1001
1002 // general magic number check
1003 magicerror = 0;
1004 if (magiccheck) {
1005 switch (i) {
1006 case 1: // image
1007 case 2: // kernel
1008 if (!(
1009 ((mtd->magic[0] == 0x27) && (mtd->magic[1] == 0x05)) // uImage
1010 )) {
1011 magicerror = 1;
1012 }
1013 break;
1014 case 3: // rootfs
1015 if (!(
1016 ((mtd->magic[0] == 0x68) && (mtd->magic[1] == 0x73)) // squashfs
1017 || ((mtd->magic[0] == 0x85) && (mtd->magic[1] == 0x19)) // jffs
1018 )) {
1019 magicerror = 1;
1020 }
1021 break;
1022 default:
1023 magicerror = 1;
1024 break;
1025 }
1026 if (magicerror) {
1027 printf("mtd %s input file %s has unknown magic number (0x%02X%02X)", mtd->name, mtd->filename, mtd->magic[0], mtd->magic[1]);
1028 if (ignoremagic) {
1029 printf("...ignoring");
1030 } else {
1031 exitcode = 1;
1032 }
1033 printf("\n");
1034 }
1035 }
1036
1037 // mtd specific size check
1038 if (mtd == &mtd_image) {
1039 if (mtd->filesize < 0x00200000) {
1040 exitcode = 1;
1041 printf("mtd %s input file %s too unrealistic small (0x%08lX)\n", mtd->name, mtd->filename, mtd->filesize);
1042 }
1043 }
1044
1045 if (mtd == &mtd_kernel) {
1046 if (mtd->filesize < 0x00080000) {
1047 exitcode = 1;
1048 printf("mtd %s input file %s too unrealistic small (0x%08lX)\n", mtd->name, mtd->filename, mtd->filesize);
1049 }
1050 }
1051
1052 // general size check
1053 if (sizecheck) {
1054 if (sizecheck <= 0) {
1055 exitcode = 1;
1056 printf("mtd %s bad file size check (%i) due to input data\n", mtd->name, sizecheck);
1057 } else {
1058 if (mtd->filesize > sizecheck) {
1059 exitcode = 1;
1060 printf("mtd %s input file %s too big (0x%08lX)\n", mtd->name, mtd->filename, mtd->filesize);
1061 }
1062 }
1063 }
1064 }
1065
1066 // Check for mandatory parts
1067 if ((!mtd_image.filename) && (!mtd_kernel.filename || !mtd_rootfs.filename)) {
1068 exitcode = 1;
1069 if (mtd_kernel.filename && !mtd_rootfs.filename) {
1070 printf("Kernel without rootfs, either incorrectly specified or not at all in parameter file\n");
1071 } else if (!mtd_kernel.filename && mtd_rootfs.filename) {
1072 printf("Rootfs without kernel, either incorrectly specified or not at all in parameter file\n");
1073 } else {
1074 printf("Neither an image nor kernel with rootfs was/were correctly specified or at all in parameter file\n");
1075 }
1076 }
1077
1078 // Check for duplicate parts
1079 if ((mtd_image.filename) && (mtd_kernel.filename || mtd_rootfs.filename)) {
1080 exitcode = 1;
1081 printf("Image and kernel/rootfs specified in parameter file\n");
1082 }
1083
1084 lprintf(DEBUG, "...done checking mtd data\n");
1085 }
1086
1087
1088 // bin creation in memory
1089 if ((!exitcode) && (par_filename)) {
1090 bin_filename = "wrt350n.bin";
1091
1092 lprintf(DEBUG, "creating bin file %s...\n", bin_filename);
1093
1094 exitcode = create_bin_file(bin_filename);
1095
1096 lprintf(DEBUG, "...done creating bin file\n");
1097 }
1098
1099 // zip file creation
1100 if ((!exitcode) && (!onlybin) && (!zip_filename)) {
1101 zip_filename = "wrt350n.zip";
1102
1103 lprintf(DEBUG, "creating zip file %s...\n", zip_filename);
1104
1105 exitcode = create_zip_file(zip_filename, bin_filename);
1106
1107 lprintf(DEBUG, "...done creating zip file\n");
1108 }
1109
1110
1111 // img file creation
1112 if ((!exitcode) && (f_img)) {
1113 lprintf(DEBUG, "creating img file...\n");
1114
1115 exitcode = create_img_file(f_img, img_filename, zip_filename);
1116
1117 lprintf(DEBUG, "...done creating img file\n");
1118 }
1119
1120 // clean up
1121 if (f_img) {
1122 fclose(f_img);
1123 }
1124
1125 // end program
1126 return exitcode;
1127 }