3 WRT350Nv2-Builder 2.3 (previously called buildimg)
4 Copyright (C) 2008-2009 Dirk Teurlings <info@upexia.nl>
5 Copyright (C) 2009-2010 Matthias Buecher (http://www.maddes.net/)
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.
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.
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
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.
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.
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
37 1 wrt350nv2.par parameter file describing the image layout
38 2 wrt350nv2.img output file for linksys style image
40 A u-boot image inside the bin file is not necessary.
41 The version is not important.
42 The name of the bin file is not important, but still "wrt350n.bin" is used to
43 keep as close as possible to the stock firmware.
45 Linksys assumes that no mtd will be used to its maximum, so the last 16 bytes
46 of the mtd are abused to define the length of the next mtd content (4 bytes for
49 At the end of "rootfs" additional 16 bytes are abused for some data and a
50 highly important eRcOmM identifier, so the last 32 bytes of "rootfs" are abused.
52 At the end of "u-boot" 128 bytes are abused for some data, a checksum and a
53 highly important sErCoMm identifier.
56 This program uses a special GNU scanf modifier to allocate
57 sufficient memory for a strings with unknown length.
58 See http://www.kernel.org/doc/man-pages/online/pages/man3/scanf.3.html#NOTES
61 To extract everything from a Linksys style firmware image see
62 https://forum.openwrt.org/viewtopic.php?pid=92928#p92928
65 v2.3 - allow jffs by adding its magic number (0x8519)
66 added parameter option -i to ignore unknown magic numbers
67 v2.2 - fixed checksum byte calculation for other versions than 0x2019
68 fixed rare problem with padsize
69 updated info to stock firmware 2.00.20
71 v2.1 - used "wrt350n.bin" for the created image (closer to stock)
72 added option to create the image in two separate steps (-b / -z)
73 v2.0 - complete re-write
78 #define _GNU_SOURCE // for GNU's basename()
80 #include <errno.h> // errno
82 #include <stdio.h> // fopen(), fread(), fclose(), etc.
83 #include <stdlib.h> // system(), etc.
84 #include <string.h> // basename(), strerror(), strdup(), etc.
85 #include <unistd.h> // optopt(), access(), etc.
87 #include <sys/wait.h> // WEXITSTATUS, etc.
90 #include "md5.h" // MD5 routines
91 #include "upgrade.h" // Linksys definitions from firmware 2.0.19 (unchanged up to 2.0.20)
96 char program_info
[] = "WRT350Nv2-Builder v%s by Dirk Teurlings <info@upexia.nl> and Matthias Buecher (http://www.maddes.net/)\n";
110 unsigned char magic
[2];
113 mtd_info mtd_kernel
= { "kernel", 0, 0, NULL
, 0L, { 0, 0 } };
114 mtd_info mtd_rootfs
= { "rootfs", 0, 0, NULL
, 0L, { 0, 0 } };
115 mtd_info mtd_uboot
= { "u-boot", 0, 0, NULL
, 0L, { 0, 0 } };
117 #define ROOTFS_END_OFFSET 0x00760000
118 #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
119 // 2.0.17: filled up to 0x00640000
120 // 2.0.19: filled up to 0x00670000
121 // 2.0.20: filled up to 0x00670000
123 // rootfs statics via: hexdump -v -e '1/1 "0x%02X, "' -s 0x0075FFE0 -n 16 "wrt350n.bin" ; echo -en "\n"
124 unsigned char product_id
[] = { 0x00, 0x03 }; // seems to be a fixed value
125 unsigned char protocol_id
[] = { 0x00, 0x00 }; // seems to be a fixed value
126 unsigned char fw_version
[] = { 0x20, 0x20 };
127 unsigned char rootfs_unknown
[] = { 0x90, 0xF7 }; // seems to be a fixed value
128 unsigned char sign
[] = { 0x65, 0x52, 0x63, 0x4F, 0x6D, 0x4D, 0x00, 0x00 }; // eRcOmM
130 // u-boot statics via: hexdump -v -e '1/1 "0x%02X, "' -s 0x007FFF80 -n 128 "wrt350n.bin" ; echo -en "\n"
131 //unsigned char sn[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; // (12) seems to be an unused value
132 //unsigned char pin[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; // (8) seems to be an unused value
133 //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
134 // 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
135 //unsigned char checksum[] = { 0xE9 }; // (1) is calculated, does it belong to node?
136 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
137 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
138 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, // protocol id?
139 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, // protocol id?
140 0x12, 0x34, // firmware version, same as in rootfs
141 0x00, 0x00, 0x00, 0x04,
142 0x73, 0x45, 0x72, 0x43, 0x6F, 0x4D, 0x6D }; // sErCoMm
144 // 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)
145 unsigned char img_hdr
[] = { 0x00, 0x01, 0x00, 0x00, 0x59, 0x42, 0x50, 0x00, 0x01, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
146 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
147 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03,
149 0x12, 0x34, // firmware version, same as in rootfs
150 0x00, 0x00, 0x00, 0x04, 0x61, 0x44, 0x6D, 0x42, 0x6C, 0x4B, 0x3D, 0x00,
151 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 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, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
154 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
155 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
156 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 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 0x12, 0x34, 0x56, 0x78, 0x9A, 0xBC, 0xDE, 0xF0, 0x12, 0x34, 0x56, 0x78, 0x9A, 0xBC, 0xDE, 0xF0, // md5 checksum
178 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
180 unsigned char img_eof
[] = { 0xFF };
183 void lprintf(int outputlevel
, char *fmt
, ...) {
185 if (outputlevel
<= verbosity
) {
193 int parse_par_file(FILE *f_par
) {
215 while (!feof(f_par
)) {
216 // read next line into memory
218 // allocate memory for input line
220 buffer
= malloc(buffer_size
);
224 printf("parse_par_file: can not allocate %i bytes\n", (int) buffer_size
);
228 line
= fgets(buffer
, buffer_size
, f_par
);
230 exitcode
= ferror(f_par
);
232 printf("parse_par_file: %s\n", strerror(exitcode
));
237 // if buffer was not completely filled, then assume that line is complete
238 count
= strlen(buffer
) + 1;
239 if (count
-- < buffer_size
) {
245 // reset file position to line start
246 value
= fseek(f_par
, -count
, SEEK_CUR
);
249 printf("parse_par_file: %s\n", strerror(exitcode
));
253 // double buffer size
257 lprintf(DEBUG_LVL2
, " extending buffer to %i bytes\n", buffer_size
);
259 if ((!line
) || (exitcode
)) {
263 lineno
++; // increase line number
265 lprintf(DEBUG_LVL2
, " line %i (%i) %s", lineno
, count
, line
);
270 // split line if starting with a colon
273 count
= sscanf(line
, ":%255s %i %255s", string1
, &value
, string2
);
275 printf("line %i does not meet defined format (:<mtdname> <mtdsize> <file>)\n", lineno
);
277 // populate mtd_info if supported mtd names
278 if (!strcmp(string1
, mtd_kernel
.name
)) {
280 } else if (!strcmp(string1
, mtd_rootfs
.name
)) {
282 } else if (!strcmp(string1
, mtd_uboot
.name
)) {
287 printf("unknown mtd %s in line %i\n", string1
, lineno
);
288 } else if (mtd
->filename
) {
290 printf("mtd %s in line %i multiple definitions\n", string1
, lineno
);
293 mtd
->filename
= strdup(string2
);
296 f_in
= fopen(mtd
->filename
, "rb");
299 printf("input file %s: %s\n", mtd
->filename
, strerror(f_exitcode
));
301 value
= fread(&mtd
->magic
, 1, 2, f_in
);
304 f_exitcode
= ferror(f_in
);
305 printf("input file %s: %s\n", mtd
->filename
, strerror(f_exitcode
));
308 printf("input file %s: smaller than two bytes, no magic code\n", mtd
->filename
);
312 value
= fseek(f_in
, 0, SEEK_END
);
315 printf("input file %s: %s\n", mtd
->filename
, strerror(f_exitcode
));
317 mtd
->filesize
= ftell(f_in
);
318 if (mtd
->filesize
== -1) {
320 printf("input file %s: %s\n", mtd
->filename
, strerror(f_exitcode
));
327 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
);
331 case '#': // integer values
332 count
= sscanf(line
, "#%255s %i", string1
, &value
);
334 printf("line %i does not meet defined format (#<variable name> <integer>\n", lineno
);
336 if (!strcmp(string1
, "version")) {
338 fw_version
[0] = 0x000000FF & ( value
>> 8 );
339 fw_version
[1] = 0x000000FF & value
;
341 printf("unknown integer variable %s in line %i\n", string1
, lineno
);
344 lprintf(DEBUG
, "integer variable %s in line %i: 0x%08X\n", string1
, lineno
, value
);
348 count
= sscanf(line
, "$%255s %255s", string1
, string2
);
350 printf("line %i does not meet defined format (:<mtdname> <mtdsize> <file>)\n", lineno
);
353 if (!strcmp(string1, "something")) {
354 something = strdup(string2);
357 printf("unknown string variable %s in line %i\n", string1
, lineno
);
359 lprintf(DEBUG
, "string variable %s in line %i: %s\n", string1
, lineno
, string2
);
369 exitcode
= f_exitcode
;
376 int create_bin_file(char *bin_filename
) {
379 unsigned char *buffer
;
386 char *rand_filename
= "/dev/urandom";
390 unsigned long int csum
;
391 unsigned char checksum
;
395 // allocate memory for bin file
396 buffer
= malloc(KERNEL_CODE_OFFSET
+ FLASH_SIZE
);
399 printf("create_bin_file: can not allocate %i bytes\n", FLASH_SIZE
);
401 // initialize with zero
402 memset(buffer
, 0, KERNEL_CODE_OFFSET
+ FLASH_SIZE
);
407 for (i
= 1; i
<= 3; i
++) {
417 addsize
= mtd
->filesize
;
418 padsize
= ROOTFS_MIN_OFFSET
- mtd_kernel
.size
- mtd
->filesize
;
422 addsize
= mtd
->filesize
;
427 printf("create_bin_file: unknown mtd %i\n", i
);
433 if (!mtd
->filename
) {
437 lprintf(DEBUG
, "adding mtd %s file %s\n", mtd
->name
, mtd
->filename
);
441 buffer
[KERNEL_CODE_OFFSET
+ mtd
->offset
- 16] = 0x000000FFL
& ( addsize
>> 24 );
442 buffer
[KERNEL_CODE_OFFSET
+ mtd
->offset
- 15] = 0x000000FFL
& ( addsize
>> 16 );
443 buffer
[KERNEL_CODE_OFFSET
+ mtd
->offset
- 14] = 0x000000FFL
& ( addsize
>> 8 );
444 buffer
[KERNEL_CODE_OFFSET
+ mtd
->offset
- 13] = 0x000000FFL
& addsize
;
447 // adding file content
448 f_in
= fopen(mtd
->filename
, "rb");
451 printf("input file %s: %s\n", mtd
->filename
, strerror(exitcode
));
453 size
= fread(&buffer
[KERNEL_CODE_OFFSET
+ mtd
->offset
], mtd
->filesize
, 1, f_in
);
456 exitcode
= ferror(f_in
);
457 printf("input file %s: %s\n", mtd
->filename
, strerror(exitcode
));
460 printf("input file %s: smaller than before *doh*\n", mtd
->filename
);
468 addsize
= padsize
& 0x0000FFFF; // start on next 64KB border
472 printf("mtd %s input file %s is too small (0x%08lX), adding 0x%08X random bytes\n", mtd
->name
, mtd
->filename
, mtd
->filesize
, padsize
);
474 addsize
+= KERNEL_CODE_OFFSET
+ mtd
->offset
+ mtd
->filesize
; // get offset
475 lprintf(DEBUG
, " padding offset 0x%08X length 0x%08X\n", addsize
, padsize
);
477 f_in
= fopen(rand_filename
, "rb");
480 printf("input file %s: %s\n", rand_filename
, strerror(exitcode
));
482 size
= fread(&buffer
[addsize
], padsize
, 1, f_in
);
485 exitcode
= ferror(f_in
);
486 printf("input file %s: %s\n", rand_filename
, strerror(exitcode
));
489 printf("input file %s: smaller than before *doh*\n", rand_filename
);
498 // add special contents
500 lprintf(DEBUG
, "adding rootfs special data\n");
501 memcpy(&buffer
[KERNEL_CODE_OFFSET
+ PRODUCT_ID_OFFSET
], product_id
, 2);
502 memcpy(&buffer
[KERNEL_CODE_OFFSET
+ PROTOCOL_ID_OFFSET
], protocol_id
, 2);
503 memcpy(&buffer
[KERNEL_CODE_OFFSET
+ FW_VERSION_OFFSET
], fw_version
, 2);
504 memcpy(&buffer
[KERNEL_CODE_OFFSET
+ FW_VERSION_OFFSET
+ 2], rootfs_unknown
, 2);
505 memcpy(&buffer
[KERNEL_CODE_OFFSET
+ SIGN_OFFSET
], sign
, 8); // eRcOmM
507 lprintf(DEBUG
, "adding u-boot special data\n");
508 // memcpy(&buffer[KERNEL_CODE_OFFSET + SN_OFF], sn, 12); // ToDo: currently zero, find out what's this for?
509 // memcpy(&buffer[KERNEL_CODE_OFFSET + PIN_OFF], pin, 8); // ToDo: currently zero, find out what's this for?
510 // memcpy(&buffer[KERNEL_CODE_OFFSET + NODE_BASE_OFF], node, 25); // ToDo: currently zero, find out what's this for?
511 memcpy(&buffer
[KERNEL_CODE_OFFSET
+ BOOT_ADDR_BASE_OFF
+ PID_OFFSET
], pid
, 70); // sErCoMm
512 memcpy(&buffer
[KERNEL_CODE_OFFSET
+ BOOT_ADDR_BASE_OFF
+ PID_OFFSET
+ 57], fw_version
, 2);
514 lprintf(DEBUG
, "adding checksum byte\n");
516 for (i
= 0; i
< KERNEL_CODE_OFFSET
+ FLASH_SIZE
; i
++) {
519 lprintf(DEBUG_LVL2
, " checksum 0x%016lX (%li)\n", csum
, csum
);
521 buffer
[KERNEL_CODE_OFFSET
+ NODE_BASE_OFF
+ 25] = ~csum
+ 1;
522 lprintf(DEBUG
, " byte 0x%02X\n", buffer
[KERNEL_CODE_OFFSET
+ NODE_BASE_OFF
+ 25]);
527 lprintf(DEBUG
, "writing file %s\n", bin_filename
);
528 f_out
= fopen(bin_filename
, "wb");
531 printf("output file %s: %s\n", bin_filename
, strerror(exitcode
));
533 size
= fwrite(buffer
, KERNEL_CODE_OFFSET
+ FLASH_SIZE
, 1, f_out
);
536 exitcode
= ferror(f_out
);
537 printf("output file %s: %s\n", bin_filename
, strerror(exitcode
));
540 printf("output file %s: unspecified write error\n", bin_filename
);
551 int create_zip_file(char *zip_filename
, char *bin_filename
) {
561 // allocate memory for command line
563 buffer
= malloc(buffer_size
);
567 printf("create_zip_file: can not allocate %i bytes\n", (int) buffer_size
);
571 // if buffer was not completely filled, then line fit in completely
572 count
= snprintf(buffer
, buffer_size
, "zip \"%s\" \"%s\"", zip_filename
, bin_filename
);
573 if ((count
> -1) && (count
< buffer_size
)) {
577 // otherwise try again with more space
578 if (count
> -1) { // glibc 2.1
579 buffer_size
= count
+ 1; // precisely what is needed
580 } else { // glibc 2.0
581 buffer_size
*= 2; // twice the old size
585 lprintf(DEBUG_LVL2
, " extending buffer to %i bytes\n", buffer_size
);
590 lprintf(DEBUG
, "%s\n", buffer
);
591 count
= system(buffer
);
592 if ((count
< 0) || (WEXITSTATUS(count
))) {
594 printf("create_zip_file: can not execute %s bytes\n", buffer
);
602 int create_img_file(FILE *f_out
, char *out_filename
, char *zip_filename
) {
606 md5_byte_t digest
[16];
612 unsigned char buffer
[1];
614 // copy firmware version
615 memcpy(&img_hdr
[50], fw_version
, 2);
617 // clear md5 checksum
618 memset(&img_hdr
[480], 0, 16);
620 // prepare md5 checksum calculation
624 lprintf(DEBUG_LVL2
, " adding img header\n");
625 for (i
= 0; i
< 512; i
++) {
626 size
= fputc(img_hdr
[i
], f_out
);
628 exitcode
= ferror(f_out
);
629 printf("output file %s: %s\n", out_filename
, strerror(exitcode
));
632 md5_append(&state
, (const md5_byte_t
*)&img_hdr
[i
], 1);
637 lprintf(DEBUG_LVL2
, " adding zip file\n");
638 f_in
= fopen(zip_filename
, "rb");
641 printf("input file %s: %s\n", zip_filename
, strerror(exitcode
));
643 while ((size
= fgetc(f_in
)) != EOF
) {
646 size
= fputc(buffer
[0], f_out
);
648 exitcode
= ferror(f_out
);
649 printf("output file %s: %s\n", out_filename
, strerror(exitcode
));
652 md5_append(&state
, (const md5_byte_t
*)buffer
, 1);
655 exitcode
= ferror(f_in
);
656 printf("input file %s: %s\n", zip_filename
, strerror(exitcode
));
664 lprintf(DEBUG_LVL2
, " adding img eof byte\n");
665 size
= fputc(img_eof
[0], f_out
);
667 exitcode
= ferror(f_out
);
668 printf("output file %s: %s\n", out_filename
, strerror(exitcode
));
670 md5_append(&state
, (const md5_byte_t
*)img_eof
, 1);
673 // append salt to md5 checksum
674 md5_append(&state
, (const md5_byte_t
*)"A^gU*<>?RFY@#DR&Z", 17);
676 // finish md5 checksum calculation
677 md5_finish(&state
, digest
);
679 // write md5 checksum into img header
681 lprintf(DEBUG_LVL2
, " writing md5 checksum into img header of file\n");
683 size
= fseek(f_out
, 480, SEEK_SET
);
686 printf("output file %s: %s\n", out_filename
, strerror(exitcode
));
688 size
= fwrite(digest
, 16, 1, f_out
);
691 exitcode
= ferror(f_out
);
692 printf("output file %s: %s\n", out_filename
, strerror(exitcode
));
695 printf("output file %s: unspecified write error\n", out_filename
);
707 int main(int argc
, char *argv
[]) {
715 char *par_filename
= NULL
;
716 char *img_filename
= NULL
;
717 char *base_filename
= NULL
;
718 char *bin_filename
= NULL
;
719 char *zip_filename
= NULL
;
733 // display program header
734 printf(program_info
, VERSION
);
737 // command line processing
743 while ((option
= getopt(argc
, argv
, "hbzif:v")) != -1) {
758 sizecheck
= sscanf(optarg
, "%i", &i
);
759 if (sizecheck
!= 1) {
760 printf("Firmware version of -f option not a valid integer\n");
763 fw_version
[0] = 0x000000FF & ( i
>> 8 );
764 fw_version
[1] = 0x000000FF & i
;
770 case ':': // option with missing operand
771 printf("Option -%c requires an operand\n", optopt
);
775 printf("Unrecognized option: -%c\n", optopt
);
782 for ( ; optind
< argc
; optind
++) {
784 par_filename
= argv
[optind
];
786 if (access(par_filename
, R_OK
)) {
788 printf("No read access to zip file %s\n", par_filename
);
790 printf("No read access to parameter or zip file %s\n", par_filename
);
798 if ((!onlybin
) && (!img_filename
)) {
799 img_filename
= argv
[optind
];
801 if (!access(img_filename
, F_OK
)) { // if file already exists then check write access
802 if (access(img_filename
, W_OK
)) {
803 printf("No write access to image file %s\n", img_filename
);
811 printf("Too many files stated\n");
819 printf("Zip file not stated\n");
821 printf("Parameter file not stated\n");
825 base_filename
= basename(par_filename
);
826 if (!base_filename
) {
828 printf("Zip file is a directory\n");
830 printf("Parameter file is a directory\n");
838 printf("Image file not stated\n");
841 base_filename
= basename(img_filename
);
842 if (!base_filename
) {
843 printf("Image file is a directory\n");
849 // check for mutually exclusive options
850 if ((onlybin
) && (havezip
)) {
851 printf("Option -b and -z are mutually exclusive\n");
855 // react on option problems or help request, then exit
856 if ((exitcode
) || (help
)) {
858 printf("This program creates Linksys style images for the WRT350Nv2 router.\n");
861 %s [-h] [-b] [-z] [-i] [-f <version>] [-v] <parameter or zip file> [<image file>]\n\n\
863 -h - Show this help\n\
864 -b - Create only bin file, no img or zip file is created\n\
865 -z - Have zip file, the img file will be directly created from it\n\
866 -i - Ignore unknown magic numbers\n\
867 -f <version> - Wanted firmware version to use with -z\n\
868 Default firmware version is 0x2020 = 2.00.20.\n\
869 Note: version from parameter file will supersede this\n\
870 -v - Increase debug verbosity level\n\n\
872 %s wrt350nv2.par wrt350nv2.img\n\n", argv
[0], argv
[0]);
876 // handle special case when zipfile is stated
878 zip_filename
= par_filename
;
882 lprintf(DEBUG_LVL2
, " Verbosity: %i\n", verbosity
);
883 lprintf(DEBUG_LVL2
, " Program: %s\n", argv
[0]);
886 lprintf(DEBUG
, "Parameter file: %s\n", par_filename
);
889 lprintf(DEBUG
, "Zip file: %s\n", zip_filename
);
892 lprintf(DEBUG
, "Image file: %s\n", img_filename
);
896 // open files from command line
897 // parameter/zip file
899 f_par
= fopen(par_filename
, "rt");
902 printf("Input file %s: %s\n", par_filename
, strerror(exitcode
));
908 f_img
= fopen(img_filename
, "wb");
911 printf("Output file %s: %s\n", img_filename
, strerror(exitcode
));
920 // parameter file processing
921 if ((!exitcode
) && (f_par
)) {
922 lprintf(DEBUG
, "parsing parameter file...\n");
924 exitcode
= parse_par_file(f_par
);
926 lprintf(DEBUG
, "...done parsing file\n");
933 // check all input data
934 if ((!exitcode
) && (par_filename
)) {
935 lprintf(DEBUG
, "checking mtd data...\n");
937 for (i
= 1; i
<= 3; i
++) {
947 sizecheck
= mtd_kernel
.size
- 16;
952 mtd
->offset
= mtd_kernel
.size
;
953 mtd
->size
= ROOTFS_END_OFFSET
- mtd_kernel
.size
;
955 sizecheck
= PRODUCT_ID_OFFSET
- mtd_kernel
.size
;
960 mtd
->offset
= BOOT_ADDR_BASE_OFF
;
962 sizecheck
= SN_OFF
- BOOT_ADDR_BASE_OFF
;
967 printf("unknown mtd check %i\n", i
);
974 lprintf(DEBUG_LVL2
, " checking mtd %s\n", mtd
->name
);
977 if ((mandatory
) && (!mtd
->filename
)) {
979 printf("mtd %s not specified correctly or at all in parameter file\n", mtd
->name
);
982 // no further checks if no file data present
983 if (!mtd
->filename
) {
987 // not updated by stock firmware
989 printf("mtd %s is specified, but will not be updated as of Linksys firmware 2.0.19\n", mtd
->name
);
992 // general magic number check
998 ((mtd
->magic
[0] == 0x27) && (mtd
->magic
[1] == 0x05)) // uImage
1005 ((mtd
->magic
[0] == 0x68) && (mtd
->magic
[1] == 0x73)) // squashfs
1006 || ((mtd
->magic
[0] == 0x85) && (mtd
->magic
[1] == 0x19)) // jffs
1016 printf("mtd %s input file %s has unknown magic number (0x%02X%02X)", mtd
->name
, mtd
->filename
, mtd
->magic
[0], mtd
->magic
[1]);
1018 printf("...ignoring");
1026 // mtd specific size check
1027 if (mtd
== &mtd_kernel
) {
1028 if (mtd
->filesize
< 0x00050000) {
1030 printf("mtd %s input file %s too unrealistic small (0x%08lX)\n", mtd
->name
, mtd
->filename
, mtd
->filesize
);
1034 // general size check
1036 if (sizecheck
<= 0) {
1038 printf("mtd %s bad file size check (%i) due to input data\n", mtd
->name
, sizecheck
);
1040 if (mtd
->filesize
> sizecheck
) {
1042 printf("mtd %s input file %s too big (0x%08lX)\n", mtd
->name
, mtd
->filename
, mtd
->filesize
);
1047 lprintf(DEBUG
, "...done checking mtd data\n");
1051 // bin creation in memory
1052 if ((!exitcode
) && (par_filename
)) {
1053 bin_filename
= "wrt350n.bin";
1055 lprintf(DEBUG
, "creating bin file %s...\n", bin_filename
);
1057 exitcode
= create_bin_file(bin_filename
);
1059 lprintf(DEBUG
, "...done creating bin file\n");
1062 // zip file creation
1063 if ((!exitcode
) && (!onlybin
) && (!zip_filename
)) {
1064 zip_filename
= "wrt350n.zip";
1066 lprintf(DEBUG
, "creating zip file %s...\n", zip_filename
);
1068 exitcode
= create_zip_file(zip_filename
, bin_filename
);
1070 lprintf(DEBUG
, "...done creating zip file\n");
1074 // img file creation
1075 if ((!exitcode
) && (f_img
)) {
1076 lprintf(DEBUG
, "creating img file...\n");
1078 exitcode
= create_img_file(f_img
, img_filename
, zip_filename
);
1080 lprintf(DEBUG
, "...done creating img file\n");