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