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