firmware-utils: mktplinkfw: add option for endianness swap
[openwrt/openwrt.git] / tools / firmware-utils / src / mkwrggimg.c
1 /*
2 * Copyright (C) 2011 Gabor Juhos <juhosg@openwrt.org>
3 * Copyright (C) 2016 Stijn Tintel <stijn@linux-ipv6.be>
4 *
5 * This program is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 as published
7 * by the Free Software Foundation.
8 *
9 */
10
11 #define _ANSI_SOURCE
12 #include <stdio.h>
13 #include <stdlib.h>
14 #include <stdint.h>
15 #include <string.h>
16 #include <unistd.h>
17 #include <libgen.h>
18 #include <getopt.h>
19 #include <stdarg.h>
20 #include <errno.h>
21 #include <sys/stat.h>
22
23 #include "md5.h"
24
25 #define ERR(fmt, ...) do { \
26 fflush(0); \
27 fprintf(stderr, "[%s] *** error: " fmt "\n", \
28 progname, ## __VA_ARGS__ ); \
29 } while (0)
30
31 #define ERRS(fmt, ...) do { \
32 int save = errno; \
33 fflush(0); \
34 fprintf(stderr, "[%s] *** error: " fmt ", %s\n", \
35 progname, ## __VA_ARGS__, strerror(save)); \
36 } while (0)
37
38 #define WRGG03_MAGIC 0x20080321
39
40 struct wrgg03_header {
41 char signature[32];
42 uint32_t magic1;
43 uint32_t magic2;
44 char version[16];
45 char model[16];
46 uint32_t flag[2];
47 uint32_t reserve[2];
48 char buildno[16];
49 uint32_t size;
50 uint32_t offset;
51 char devname[32];
52 char digest[16];
53 } __attribute__ ((packed));
54
55 static char *progname;
56 static char *ifname;
57 static char *ofname;
58 static char *signature;
59 static char *version;
60 static char *model;
61 static uint32_t flag = 0;
62 static uint32_t reserve = 0;
63 static char *buildno;
64 static uint32_t offset;
65 static char *devname;
66 static int big_endian;
67
68 void usage(int status)
69 {
70 FILE *stream = (status != EXIT_SUCCESS) ? stderr : stdout;
71
72 fprintf(stream, "Usage: %s [OPTIONS...]\n", progname);
73 fprintf(stream,
74 "\n"
75 "Options:\n"
76 " -b create image in big endian format\n"
77 " -B <buildno> build number\n"
78 " -i <file> read input from the file <file>\n"
79 " -d <name> set device name to <name>\n"
80 " -m <model> model name\n"
81 " -o <file> write output to the file <file>\n"
82 " -O <offset> set offset to <offset>\n"
83 " -s <sig> set image signature to <sig>\n"
84 " -h show this screen\n"
85 );
86
87 exit(status);
88 }
89
90 static void put_u32(void *data, uint32_t val, int swap)
91 {
92 unsigned char *p = data;
93
94 if (swap) {
95 p[0] = (val >> 24) & 0xff;
96 p[1] = (val >> 16) & 0xff;
97 p[2] = (val >> 8) & 0xff;
98 p[3] = val & 0xff;
99 } else {
100 p[3] = (val >> 24) & 0xff;
101 p[2] = (val >> 16) & 0xff;
102 p[1] = (val >> 8) & 0xff;
103 p[0] = val & 0xff;
104 }
105 }
106
107 static void get_digest(struct wrgg03_header *header, char *data, int size)
108 {
109 MD5_CTX ctx;
110
111 MD5_Init(&ctx);
112
113 MD5_Update(&ctx, (char *)&header->offset, sizeof(header->offset));
114 MD5_Update(&ctx, (char *)&header->devname, sizeof(header->devname));
115 MD5_Update(&ctx, data, size);
116
117 MD5_Final(header->digest, &ctx);
118 }
119
120 int main(int argc, char *argv[])
121 {
122 struct wrgg03_header *header;
123 char *buf;
124 struct stat st;
125 int buflen;
126 int err;
127 int res = EXIT_FAILURE;
128
129 FILE *outfile, *infile;
130
131 progname = basename(argv[0]);
132
133 while ( 1 ) {
134 int c;
135
136 c = getopt(argc, argv, "bd:i:m:o:s:v:B:O:h");
137 if (c == -1)
138 break;
139
140 switch (c) {
141 case 'b':
142 big_endian = 1;
143 break;
144 case 'B':
145 buildno = optarg;
146 break;
147 case 'd':
148 devname = optarg;
149 break;
150 case 'i':
151 ifname = optarg;
152 break;
153 case 'm':
154 model = optarg;
155 break;
156 case 'o':
157 ofname = optarg;
158 break;
159 case 's':
160 signature = optarg;
161 break;
162 case 'v':
163 version = optarg;
164 break;
165 case 'O':
166 offset = strtoul(optarg, NULL, 0);
167 break;
168 case 'h':
169 usage(EXIT_SUCCESS);
170 break;
171
172 default:
173 usage(EXIT_FAILURE);
174 break;
175 }
176 }
177
178 if (signature == NULL) {
179 ERR("no signature specified");
180 goto err;
181 }
182
183 if (ifname == NULL) {
184 ERR("no input file specified");
185 goto err;
186 }
187
188 if (ofname == NULL) {
189 ERR("no output file specified");
190 goto err;
191 }
192
193 if (devname == NULL) {
194 ERR("no device name specified");
195 goto err;
196 }
197
198 if (model == NULL) {
199 ERR("no model name specified");
200 goto err;
201 }
202
203 if (buildno == NULL) {
204 ERR("no build number specified");
205 goto err;
206 }
207
208 if (version == NULL) {
209 ERR("no version specified");
210 goto err;
211 }
212
213 err = stat(ifname, &st);
214 if (err){
215 ERRS("stat failed on %s", ifname);
216 goto err;
217 }
218
219 buflen = st.st_size + sizeof(struct wrgg03_header);
220 buf = malloc(buflen);
221 if (!buf) {
222 ERR("no memory for buffer\n");
223 goto err;
224 }
225
226 infile = fopen(ifname, "r");
227 if (infile == NULL) {
228 ERRS("could not open \"%s\" for reading", ifname);
229 goto err_free;
230 }
231
232 errno = 0;
233 fread(buf + sizeof(struct wrgg03_header), st.st_size, 1, infile);
234 if (errno != 0) {
235 ERRS("unable to read from file %s", ifname);
236 goto close_in;
237 }
238
239 header = (struct wrgg03_header *) buf;
240 memset(header, '\0', sizeof(struct wrgg03_header));
241
242 strncpy(header->signature, signature, sizeof(header->signature));
243 put_u32(&header->magic1, WRGG03_MAGIC, 0);
244 put_u32(&header->magic2, WRGG03_MAGIC, 0);
245 strncpy(header->version, version, sizeof(header->version));
246 strncpy(header->model, model, sizeof(header->model));
247 put_u32(&header->flag, flag, 0);
248 put_u32(&header->reserve, reserve, 0);
249 strncpy(header->buildno, buildno, sizeof(header->buildno));
250 put_u32(&header->size, st.st_size, big_endian);
251 put_u32(&header->offset, offset, big_endian);
252 strncpy(header->devname, devname, sizeof(header->devname));
253
254 get_digest(header, buf + sizeof(struct wrgg03_header), st.st_size);
255
256 outfile = fopen(ofname, "w");
257 if (outfile == NULL) {
258 ERRS("could not open \"%s\" for writing", ofname);
259 goto close_in;
260 }
261
262 errno = 0;
263 fwrite(buf, buflen, 1, outfile);
264 if (errno) {
265 ERRS("unable to write to file %s", ofname);
266 goto close_out;
267 }
268
269 fflush(outfile);
270
271 res = EXIT_SUCCESS;
272
273 close_out:
274 fclose(outfile);
275 if (res != EXIT_SUCCESS)
276 unlink(ofname);
277 close_in:
278 fclose(infile);
279 err_free:
280 free(buf);
281 err:
282 return res;
283 }